Skip to content

Commit 45d06df

Browse files
authored
feat(sidekick/rust): support grpc_client option (#7492)
For googleapis/google-cloud-rust#5991 .
1 parent bb4aa18 commit 45d06df

11 files changed

Lines changed: 137 additions & 6 deletions

File tree

doc/config-schema.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ This document describes the schema for the librarian.yaml.
570570
| `include_bidi_streaming_methods` | bool (optional) | Indicates whether to include gRPC bi-directional streaming methods. |
571571
| `include_server_streaming_methods` | bool (optional) | Indicates whether to include gRPC server-side streaming methods. |
572572
| `allow_streaming_any_types` | list of string | Is a list of protobuf field/message IDs with google.protobuf.Any permitted in streaming RPCs (their fields will be dropped in prost conversion). |
573+
| `grpc_client` | string | Is the Rust type used for the inner gRPC client in generated transports. Defaults to "gaxi::grpc::Client". |
573574

574575
## RustDocumentationOverride Configuration
575576

@@ -583,6 +584,7 @@ This document describes the schema for the librarian.yaml.
583584

584585
| Field | Type | Description |
585586
| :--- | :--- | :--- |
587+
| `grpc_client` | string | Is the Rust type used for the inner gRPC client in generated transports. This overrides the crate-level setting. Defaults to "gaxi::grpc::Client". |
586588
| `disabled_rustdoc_warnings` | yaml.StringSlice | Specifies rustdoc lints to disable. An empty slice explicitly enables all warnings. |
587589
| `detailed_tracing_attributes` | bool (optional) | Indicates whether to include detailed tracing attributes. This overrides the crate-level setting. |
588590
| `lro_stub_options` | bool (optional) | Indicates whether to include LRO poller options in generated stub traits. This overrides the crate-level setting. |

internal/config/language.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,20 @@ type RustDefault struct {
133133
// AllowStreamingAnyTypes is a list of protobuf field/message IDs with google.protobuf.Any
134134
// permitted in streaming RPCs (their fields will be dropped in prost conversion).
135135
AllowStreamingAnyTypes []string `yaml:"allow_streaming_any_types,omitempty"`
136+
137+
// GrpcClient is the Rust type used for the inner gRPC client in generated transports.
138+
// Defaults to "gaxi::grpc::Client".
139+
GrpcClient string `yaml:"grpc_client,omitempty"`
136140
}
137141

138142
// RustModule defines a generation target within a veneer crate.
139143
// Each module specifies what proto source to use, which template to apply,
140144
// and where to output the generated code.
141145
type RustModule struct {
146+
// GrpcClient is the Rust type used for the inner gRPC client in generated transports.
147+
// This overrides the crate-level setting. Defaults to "gaxi::grpc::Client".
148+
GrpcClient string `yaml:"grpc_client,omitempty"`
149+
142150
// DisabledRustdocWarnings specifies rustdoc lints to disable. An empty slice explicitly enables all warnings.
143151
DisabledRustdocWarnings yaml.StringSlice `yaml:"disabled_rustdoc_warnings,omitempty"`
144152

internal/librarian/rust/codec.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ func buildCodec(library *config.Library, releaseLevel string) map[string]string
171171
if rust.QuickstartServiceOverride != "" {
172172
codec["quickstart-service-override"] = rust.QuickstartServiceOverride
173173
}
174+
if rust.GrpcClient != "" {
175+
codec["grpc-client"] = rust.GrpcClient
176+
}
174177
return codec
175178
}
176179

@@ -368,5 +371,15 @@ func buildModuleCodec(library *config.Library, module *config.RustModule) map[st
368371
if module.InternalBuilders {
369372
codec["internal-builders"] = "true"
370373
}
374+
grpcClient := ""
375+
if library.Rust != nil {
376+
grpcClient = library.Rust.GrpcClient
377+
}
378+
if module.GrpcClient != "" {
379+
grpcClient = module.GrpcClient
380+
}
381+
if grpcClient != "" {
382+
codec["grpc-client"] = grpcClient
383+
}
371384
return codec
372385
}

internal/librarian/rust/codec_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,6 +1439,7 @@ func TestBuildCodec(t *testing.T) {
14391439
Ignore: true,
14401440
},
14411441
},
1442+
GrpcClient: "crate::storage::bidi::GrpcClient",
14421443
},
14431444
ModulePath: "gcs",
14441445
TemplateOverride: "custom-template",
@@ -1467,6 +1468,7 @@ func TestBuildCodec(t *testing.T) {
14671468
"generate-rpc-samples": "true",
14681469
"name-overrides": "foo=bar",
14691470
"quickstart-service-override": "OverriddenService",
1471+
"grpc-client": "crate::storage::bidi::GrpcClient",
14701472
"default-features": "feature1,feature2",
14711473
"disabled-rustdoc-warnings": "warning1,warning2",
14721474
"disabled-clippy-warnings": "clippy1,clippy2",
@@ -1535,3 +1537,52 @@ func TestModuleToModelConfig_SkippedIds(t *testing.T) {
15351537
}
15361538
})
15371539
}
1540+
1541+
func TestBuildModuleCodec_GrpcClient(t *testing.T) {
1542+
t.Run("module level grpc_client", func(t *testing.T) {
1543+
library := &config.Library{
1544+
Name: "google-cloud-storage",
1545+
}
1546+
module := &config.RustModule{
1547+
GrpcClient: "crate::storage::bidi::GrpcClient",
1548+
}
1549+
got := buildModuleCodec(library, module)
1550+
if got["grpc-client"] != "crate::storage::bidi::GrpcClient" {
1551+
t.Errorf("expected grpc-client to be %q, got %q", "crate::storage::bidi::GrpcClient", got["grpc-client"])
1552+
}
1553+
})
1554+
1555+
t.Run("crate level fallback grpc_client", func(t *testing.T) {
1556+
library := &config.Library{
1557+
Name: "google-cloud-storage",
1558+
Rust: &config.RustCrate{
1559+
RustDefault: config.RustDefault{
1560+
GrpcClient: "crate::storage::bidi::GrpcClient",
1561+
},
1562+
},
1563+
}
1564+
module := &config.RustModule{}
1565+
got := buildModuleCodec(library, module)
1566+
if got["grpc-client"] != "crate::storage::bidi::GrpcClient" {
1567+
t.Errorf("expected grpc-client to be %q, got %q", "crate::storage::bidi::GrpcClient", got["grpc-client"])
1568+
}
1569+
})
1570+
1571+
t.Run("module overrides crate level grpc_client", func(t *testing.T) {
1572+
library := &config.Library{
1573+
Name: "google-cloud-storage",
1574+
Rust: &config.RustCrate{
1575+
RustDefault: config.RustDefault{
1576+
GrpcClient: "crate::storage::default::GrpcClient",
1577+
},
1578+
},
1579+
}
1580+
module := &config.RustModule{
1581+
GrpcClient: "crate::storage::bidi::GrpcClient",
1582+
}
1583+
got := buildModuleCodec(library, module)
1584+
if got["grpc-client"] != "crate::storage::bidi::GrpcClient" {
1585+
t.Errorf("expected grpc-client to be %q, got %q", "crate::storage::bidi::GrpcClient", got["grpc-client"])
1586+
}
1587+
})
1588+
}

internal/sidekick/rust/annotate_model.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ type modelAnnotations struct {
9797
LroStubOptions bool
9898
// If true, the generated builders's visibility should be restricted to the crate.
9999
InternalBuilders bool
100+
// The Rust type used for the inner gRPC client in generated transports.
101+
GrpcClient string
100102
// The service to use for the package-level quickstart sample.
101103
// Rust generation may decide not to generate some services,
102104
// e.g. if the methods have no bindings. On occasion the service
@@ -366,6 +368,7 @@ func annotateModel(model *api.API, codec *codec) (*modelAnnotations, error) {
366368
DetailedTracingAttributes: codec.detailedTracingAttributes,
367369
LroStubOptions: codec.lroStubOptions,
368370
InternalBuilders: codec.internalBuilders,
371+
GrpcClient: codec.grpcClient,
369372
QuickstartService: quickstartService,
370373
}
371374

internal/sidekick/rust/annotate_model_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,38 @@ func TestInternalBuildersAnnotation(t *testing.T) {
188188
}
189189
}
190190

191+
func TestGrpcClientAnnotation(t *testing.T) {
192+
for _, test := range []struct {
193+
Options map[string]string
194+
Want string
195+
}{
196+
{
197+
Options: map[string]string{},
198+
Want: "gaxi::grpc::Client",
199+
},
200+
{
201+
Options: map[string]string{
202+
"grpc-client": "crate::storage::bidi::GrpcClient",
203+
},
204+
Want: "crate::storage::bidi::GrpcClient",
205+
},
206+
} {
207+
model := newTestAnnotateModelAPI()
208+
codec := newTestCodec(t, libconfig.SpecProtobuf, "", test.Options)
209+
got, err := annotateModel(model, codec)
210+
if err != nil {
211+
t.Fatal(err)
212+
}
213+
if got.GrpcClient != test.Want {
214+
t.Errorf("mismatch in GrpcClient, want=%v, got=%v", test.Want, got.GrpcClient)
215+
}
216+
svcAnn := model.Services[0].Codec.(*serviceAnnotations)
217+
if svcAnn.GrpcClient != test.Want {
218+
t.Errorf("mismatch in service GrpcClient, want=%v, got=%v", test.Want, svcAnn.GrpcClient)
219+
}
220+
}
221+
}
222+
191223
func TestQuickstartServiceAnnotation(t *testing.T) {
192224
t.Run("survives filtering", func(t *testing.T) {
193225
model := newTestAnnotateModelAPI()
@@ -375,6 +407,7 @@ func TestPackageNames(t *testing.T) {
375407
GenerateSetterSamples: true,
376408
GenerateRpcSamples: true,
377409
DetailedTracingAttributes: true,
410+
GrpcClient: "gaxi::grpc::Client",
378411
}
379412
if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(modelAnnotations{}, "BoilerPlate")); diff != "" {
380413
t.Errorf("mismatch (-want +got):\n%s", diff)

internal/sidekick/rust/annotate_service.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ type serviceAnnotations struct {
5656
DetailedTracingAttributes bool
5757
// If true, the generated builders's visibility should be restricted to the crate.
5858
InternalBuilders bool
59+
// The Rust type used for the inner gRPC client in generated transports.
60+
GrpcClient string
5961
}
6062

6163
// HasGrpc returns true if the service has at least one generated gRPC method.
@@ -218,6 +220,7 @@ func (c *codec) annotateService(s *api.Service) (*serviceAnnotations, error) {
218220
Incomplete: slices.ContainsFunc(s.Methods, func(m *api.Method) bool { return !c.generateMethod(m) }),
219221
DetailedTracingAttributes: c.detailedTracingAttributes,
220222
InternalBuilders: c.internalBuilders,
223+
GrpcClient: c.grpcClient,
221224
}
222225
s.Codec = ann
223226
return ann, nil

internal/sidekick/rust/annotate_service_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ func TestServiceAnnotationsPerServiceFeatures(t *testing.T) {
8989
ModuleName: "resource_service",
9090
PerServiceFeatures: true,
9191
Incomplete: true,
92+
GrpcClient: "gaxi::grpc::Client",
9293
}
9394
if diff := cmp.Diff(wantService, service.Codec, cmpopts.IgnoreFields(serviceAnnotations{}, "Methods")); diff != "" {
9495
t.Errorf("mismatch (-want +got):\n%s", diff)
@@ -245,6 +246,7 @@ func TestServiceAnnotationsLROTypes(t *testing.T) {
245246
resource,
246247
empty,
247248
},
249+
GrpcClient: "gaxi::grpc::Client",
248250
}
249251
if !wantService.HasLROs() {
250252
t.Errorf("HasLRO should be true. The service has several LROs.")
@@ -275,6 +277,7 @@ func TestServiceAnnotationsNameOverrides(t *testing.T) {
275277
Name: "Renamed",
276278
ModuleName: "renamed",
277279
Incomplete: true,
280+
GrpcClient: "gaxi::grpc::Client",
278281
}
279282
if diff := cmp.Diff(wantService, service.Codec, serviceFilter); diff != "" {
280283
t.Errorf("mismatch (-want +got):\n%s", diff)
@@ -312,6 +315,7 @@ func TestServiceAnnotations(t *testing.T) {
312315
PackageModuleName: "test::v1",
313316
ModuleName: "resource_service",
314317
Incomplete: true,
318+
GrpcClient: "gaxi::grpc::Client",
315319
}
316320
if diff := cmp.Diff(wantService, service.Codec, cmpopts.IgnoreFields(serviceAnnotations{}, "Methods")); diff != "" {
317321
t.Errorf("mismatch (-want +got):\n%s", diff)

internal/sidekick/rust/codec.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ func newCodec(specificationFormat string, options map[string]string) (*codec, er
7676
systemParameters: sysParams,
7777
serializeEnumsAsStrings: specificationFormat != libconfig.SpecProtobuf,
7878
bytesUseUrlSafeAlphabet: specificationFormat == libconfig.SpecDiscovery,
79+
grpcClient: "gaxi::grpc::Client",
7980
}
8081

8182
for key, definition := range options {
@@ -212,6 +213,8 @@ func newCodec(specificationFormat string, options map[string]string) (*codec, er
212213
return nil, fmt.Errorf("cannot convert `include-rpc-status-conversion` value %q to boolean: %w", definition, err)
213214
}
214215
codec.includeRpcStatusConversion = value
216+
case key == "grpc-client":
217+
codec.grpcClient = definition
215218
default:
216219
return nil, fmt.Errorf("unknown Rust codec option %q", key)
217220
}
@@ -372,6 +375,8 @@ type codec struct {
372375
internalBuilders bool
373376
// Overrides the default heuristically selected service for the package-level quickstart.
374377
quickstartServiceOverride string
378+
// The Rust type used for the inner gRPC client in generated transports.
379+
grpcClient string
375380
}
376381

377382
type systemParameter struct {

internal/sidekick/rust/codec_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,15 @@ func TestParseOptions(t *testing.T) {
369369
c.quickstartServiceOverride = "OverriddenService"
370370
},
371371
},
372+
{
373+
Format: libconfig.SpecProtobuf,
374+
Options: map[string]string{
375+
"grpc-client": "custom::GrpcClient",
376+
},
377+
Update: func(c *codec) {
378+
c.grpcClient = "custom::GrpcClient"
379+
},
380+
},
372381
} {
373382
t.Run(fmt.Sprintf("case_%d", i), func(t *testing.T) {
374383
want, err := newCodec(test.Format, map[string]string{})

0 commit comments

Comments
 (0)