Skip to content

Commit 43c8afb

Browse files
authored
feat(internal/config): add language to rust module (#3541)
Add `language`, `specification_format` to rust module config. These two params need to be override when generating the special storage control library. Add the following config to `google-cloud-storage` in google-cloud-rust after merging this PR: ``` - language: rust_storage output: src/storage/src/control/generated specification_format: none ``` Fixes #3532
1 parent c95b461 commit 43c8afb

6 files changed

Lines changed: 171 additions & 3 deletions

File tree

internal/config/language.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ type RustModule struct {
7979
// IncludeList is a list of proto files to include (e.g., "date.proto,expr.proto").
8080
IncludeList string `yaml:"include_list,omitempty"`
8181

82+
// Language can be used to select a variation of the Rust generator.
83+
// For example, `rust_storage` enables special handling for the storage client.
84+
Language string `yaml:"language,omitempty"`
85+
8286
// ModulePath is the Rust module path for converters
8387
// (e.g., "crate::generated::gapic::model").
8488
ModulePath string `yaml:"module_path,omitempty"`
@@ -104,6 +108,9 @@ type RustModule struct {
104108
// SkippedIds is a list of proto IDs to skip in generation.
105109
SkippedIds []string `yaml:"skipped_ids,omitempty"`
106110

111+
// SpecificationFormat overrides the library-level specification format.
112+
SpecificationFormat string `yaml:"specification_format,omitempty"`
113+
107114
// Source is the proto path to generate from (e.g., "google/storage/v2").
108115
Source string `yaml:"source"`
109116

internal/librarian/rust/codec.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,20 @@ func moduleToSidekickConfig(library *config.Library, module *config.RustModule,
249249
}
250250

251251
language := "rust"
252-
if module.Template == "prost" {
252+
if module.Language != "" {
253+
language = module.Language
254+
} else if module.Template == "prost" {
253255
language = "rust+prost"
254256
}
257+
258+
specificationFormat := "protobuf"
259+
if module.SpecificationFormat != "" {
260+
specificationFormat = module.SpecificationFormat
261+
}
255262
sidekickCfg := &sidekickconfig.Config{
256263
General: sidekickconfig.GeneralConfig{
257264
Language: language,
258-
SpecificationFormat: "protobuf",
265+
SpecificationFormat: specificationFormat,
259266
ServiceConfig: module.ServiceConfig,
260267
SpecificationSource: module.Source,
261268
},

internal/librarian/rust/codec_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,62 @@ func TestToSidekickConfig(t *testing.T) {
707707
},
708708
},
709709
},
710+
{
711+
name: "with custom module language",
712+
library: &config.Library{
713+
Name: "google-cloud-showcase",
714+
Rust: &config.RustCrate{
715+
Modules: []*config.RustModule{
716+
{
717+
Language: "rust_storage",
718+
},
719+
},
720+
},
721+
},
722+
want: &sidekickconfig.Config{
723+
General: sidekickconfig.GeneralConfig{
724+
Language: "rust_storage",
725+
SpecificationFormat: "protobuf",
726+
},
727+
},
728+
},
729+
{
730+
name: "with custom module specification format",
731+
library: &config.Library{
732+
Name: "google-cloud-showcase",
733+
Rust: &config.RustCrate{
734+
Modules: []*config.RustModule{
735+
{
736+
SpecificationFormat: "none",
737+
},
738+
},
739+
},
740+
},
741+
want: &sidekickconfig.Config{
742+
General: sidekickconfig.GeneralConfig{
743+
Language: "rust",
744+
SpecificationFormat: "none",
745+
},
746+
},
747+
},
748+
{
749+
name: "with prost as module template",
750+
library: &config.Library{
751+
Name: "google-cloud-showcase",
752+
Rust: &config.RustCrate{
753+
Modules: []*config.RustModule{
754+
{
755+
Template: "prost",
756+
},
757+
},
758+
},
759+
},
760+
want: &sidekickconfig.Config{
761+
General: sidekickconfig.GeneralConfig{
762+
Language: "rust+prost",
763+
},
764+
},
765+
},
710766
} {
711767
t.Run(test.name, func(t *testing.T) {
712768
if test.library.Rust != nil && test.library.Rust.Modules != nil {

internal/librarian/rust/generate.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ func generateVeneer(ctx context.Context, library *config.Library, googleapisDir,
8585
switch sidekickConfig.General.Language {
8686
case "rust":
8787
err = sidekickrust.Generate(ctx, model, module.Output, sidekickConfig)
88+
case "rust_storage":
89+
return generateRustStorage(ctx, library, module.Output, googleapisDir, protobufSrcDir)
8890
case "rust+prost":
8991
err = rust_prost.Generate(ctx, model, module.Output, sidekickConfig)
9092
default:
@@ -150,3 +152,43 @@ func DeriveChannelPath(name string) string {
150152
func DefaultOutput(channel, defaultOutput string) string {
151153
return filepath.Join(defaultOutput, strings.TrimPrefix(channel, "google/"))
152154
}
155+
156+
// generateRustStorage generates rust StorageControl client.
157+
//
158+
// The StorageControl client depends on multiple specification sources.
159+
// We load them both here, and pass them along to `rust.GenerateStorage` which will merge them appropriately.
160+
func generateRustStorage(ctx context.Context, library *config.Library, moduleOutput, googleapisDir, protobufSrcDir string) error {
161+
output := "src/storage/src/generated/gapic"
162+
storageModule := findModuleByOutput(library, output)
163+
if storageModule == nil {
164+
return fmt.Errorf("could not find module with output %s in library %s", output, library.Name)
165+
}
166+
storageConfig := moduleToSidekickConfig(library, storageModule, googleapisDir, protobufSrcDir)
167+
storageModel, err := parser.CreateModel(storageConfig)
168+
if err != nil {
169+
return fmt.Errorf("failed to create storage model: %w", err)
170+
}
171+
172+
output = "src/storage/src/generated/gapic_control"
173+
controlModule := findModuleByOutput(library, "src/storage/src/generated/gapic_control")
174+
if controlModule == nil {
175+
return fmt.Errorf("could not find module with output %s in library %s", output, library.Name)
176+
}
177+
controlConfig := moduleToSidekickConfig(library, controlModule, googleapisDir, protobufSrcDir)
178+
controlModel, err := parser.CreateModel(controlConfig)
179+
if err != nil {
180+
return fmt.Errorf("failed to create control model: %w", err)
181+
}
182+
183+
return sidekickrust.GenerateStorage(ctx, moduleOutput, storageModel, storageConfig, controlModel, controlConfig)
184+
}
185+
186+
func findModuleByOutput(library *config.Library, output string) *config.RustModule {
187+
for _, module := range library.Rust.Modules {
188+
if module.Output == output {
189+
return module
190+
}
191+
}
192+
193+
return nil
194+
}

internal/librarian/rust/generate_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,59 @@ func TestDeriveChannelPath(t *testing.T) {
302302
})
303303
}
304304
}
305+
306+
func TestFindModuleByOutput(t *testing.T) {
307+
for _, test := range []struct {
308+
name string
309+
lib *config.Library
310+
output string
311+
want *config.RustModule
312+
}{
313+
{
314+
name: "find the module",
315+
lib: &config.Library{
316+
Name: "test",
317+
Rust: &config.RustCrate{
318+
Modules: []*config.RustModule{
319+
{
320+
Language: "target-language",
321+
Output: "target-output",
322+
},
323+
{
324+
Language: "other-language",
325+
Output: "other-output",
326+
},
327+
},
328+
},
329+
},
330+
output: "target-output",
331+
want: &config.RustModule{
332+
Language: "target-language",
333+
Output: "target-output",
334+
},
335+
},
336+
{
337+
name: "does not find the module",
338+
lib: &config.Library{
339+
Name: "test",
340+
Rust: &config.RustCrate{
341+
Modules: []*config.RustModule{
342+
{
343+
Language: "other-language",
344+
Output: "other-output",
345+
},
346+
},
347+
},
348+
},
349+
output: "target-output",
350+
want: nil,
351+
},
352+
} {
353+
t.Run(test.name, func(t *testing.T) {
354+
got := findModuleByOutput(test.lib, test.output)
355+
if diff := cmp.Diff(test.want, got); diff != "" {
356+
t.Errorf("mismatch (-want +got):\n%s", diff)
357+
}
358+
})
359+
}
360+
}

internal/sidekick/config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
"path"
2424

2525
"github.com/googleapis/librarian/internal/sidekick/license"
26-
toml "github.com/pelletier/go-toml/v2"
26+
"github.com/pelletier/go-toml/v2"
2727
)
2828

2929
const (

0 commit comments

Comments
 (0)