Skip to content

Commit c8223b7

Browse files
feat(sidekick/rust): Support overriding the service for the package QuickStart (#4319)
This is useful in several cases. including if the service selected by the heuristic is under a feature flag, or not generated in Rust, or even if it's a very secondary/minor service in the package.
1 parent f432ac3 commit c8223b7

8 files changed

Lines changed: 68 additions & 8 deletions

File tree

doc/config-schema.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ This document describes the schema for the librarian.yaml.
188188
| `pagination_overrides` | list of [RustPaginationOverride](#rustpaginationoverride-configuration) | Contains overrides for pagination configuration. |
189189
| `name_overrides` | string | Contains codec-level overrides for type and service names. |
190190
| `discovery` | [RustDiscovery](#rustdiscovery-configuration) (optional) | Contains discovery-specific configuration for LRO polling. |
191+
| `quickstart_service_override` | string | Overrides the default heuristically selected service for the package-level quickstart. |
191192

192193
## RustDefault Configuration
193194

internal/config/language.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ type RustCrate struct {
221221

222222
// Discovery contains discovery-specific configuration for LRO polling.
223223
Discovery *RustDiscovery `yaml:"discovery,omitempty"`
224+
225+
// QuickstartServiceOverride overrides the default heuristically selected service for the package-level quickstart.
226+
QuickstartServiceOverride string `yaml:"quickstart_service_override,omitempty"`
224227
}
225228

226229
// RustPackageDependency represents a package dependency configuration.

internal/librarian/rust/codec.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ func buildCodec(library *config.Library) map[string]string {
155155
if rust.NameOverrides != "" {
156156
codec["name-overrides"] = rust.NameOverrides
157157
}
158+
if rust.QuickstartServiceOverride != "" {
159+
codec["quickstart-service-override"] = rust.QuickstartServiceOverride
160+
}
158161
return codec
159162
}
160163

internal/librarian/rust/codec_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,7 @@ func TestBuildCodec(t *testing.T) {
10771077
HasVeneer: true,
10781078
RoutingRequired: true,
10791079
NameOverrides: "foo=bar",
1080+
QuickstartServiceOverride: "OverriddenService",
10801081
DefaultFeatures: []string{"feature1", "feature2"},
10811082
DisabledClippyWarnings: []string{"clippy1", "clippy2"},
10821083
},
@@ -1093,6 +1094,7 @@ func TestBuildCodec(t *testing.T) {
10931094
"generate-setter-samples": "true",
10941095
"generate-rpc-samples": "true",
10951096
"name-overrides": "foo=bar",
1097+
"quickstart-service-override": "OverriddenService",
10961098
"default-features": "feature1,feature2",
10971099
"disabled-rustdoc-warnings": "warning1,warning2",
10981100
"disabled-clippy-warnings": "clippy1,clippy2",

internal/sidekick/rust/annotate.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -689,14 +689,20 @@ func annotateModel(model *api.API, codec *codec) (*modelAnnotations, error) {
689689
}()
690690

691691
var quickstartService *api.Service
692-
if model.QuickstartService != nil {
692+
if codec.quickstartServiceOverride != "" {
693+
idx := slices.IndexFunc(servicesSubset, func(s *api.Service) bool {
694+
return strings.EqualFold(codec.ServiceName(s), codec.quickstartServiceOverride) || strings.EqualFold(s.Name, codec.quickstartServiceOverride)
695+
})
696+
if idx != -1 {
697+
quickstartService = servicesSubset[idx]
698+
} else {
699+
return nil, fmt.Errorf("quickstart_service_override %q not found in generated services for package %q", codec.quickstartServiceOverride, codec.packageName(model))
700+
}
701+
} else if model.QuickstartService != nil {
693702
if slices.ContainsFunc(servicesSubset, func(s *api.Service) bool { return s == model.QuickstartService }) {
694703
quickstartService = model.QuickstartService
695704
}
696705
}
697-
if quickstartService == nil && len(servicesSubset) > 0 {
698-
quickstartService = servicesSubset[0]
699-
}
700706

701707
ann := &modelAnnotations{
702708
PackageName: codec.packageName(model),

internal/sidekick/rust/annotate_model_test.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,16 +235,48 @@ func TestQuickstartServiceAnnotation(t *testing.T) {
235235
t.Fatal(err)
236236
}
237237

238+
if got.QuickstartService != nil {
239+
t.Errorf("expected QuickstartService to be nil because it was filtered out and there is no override, got %v", got.QuickstartService.Name)
240+
}
241+
})
242+
243+
t.Run("with override", func(t *testing.T) {
244+
model := newTestAnnotateModelAPI()
245+
model.QuickstartService = model.Services[0] // Set default to 0
246+
247+
codec := newTestCodec(t, libconfig.SpecProtobuf, "", nil)
248+
// Set override to Service1
249+
codec.quickstartServiceOverride = "Service1"
250+
251+
got, err := annotateModel(model, codec)
252+
if err != nil {
253+
t.Fatal(err)
254+
}
255+
238256
if got.QuickstartService == nil {
239257
t.Fatal("QuickstartService should not be nil")
240258
}
241-
// It should have fallen back to the first non-filtered service (Service0).
242-
if got.QuickstartService != model.Services[0] {
243-
t.Errorf("expected QuickstartService to fall back to Service0, got %v", got.QuickstartService.Name)
259+
if got.QuickstartService != model.Services[1] {
260+
t.Errorf("expected QuickstartService to be overridden to Service1, got %v", got.QuickstartService.Name)
244261
}
245262
})
246-
}
247263

264+
t.Run("with missing override", func(t *testing.T) {
265+
model := newTestAnnotateModelAPI()
266+
267+
codec := newTestCodec(t, libconfig.SpecProtobuf, "", nil)
268+
codec.quickstartServiceOverride = "NonExistentService"
269+
270+
_, err := annotateModel(model, codec)
271+
if err == nil {
272+
t.Fatal("expected error, got nil")
273+
}
274+
expectedErr := `quickstart_service_override "NonExistentService" not found in generated services for package "google-cloud-Test"`
275+
if err.Error() != expectedErr {
276+
t.Errorf("expected error %q, got %q", expectedErr, err.Error())
277+
}
278+
})
279+
}
248280
func newTestAnnotateModelAPI() *api.API {
249281
service0 := &api.Service{
250282
Name: "Service0",

internal/sidekick/rust/codec.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ func newCodec(specificationFormat string, options map[string]string) (*codec, er
181181
return nil, fmt.Errorf("cannot convert `internal-builders` value %q to boolean: %w", definition, err)
182182
}
183183
codec.internalBuilders = value
184+
case key == "quickstart-service-override":
185+
codec.quickstartServiceOverride = definition
184186
default:
185187
return nil, fmt.Errorf("unknown Rust codec option %q", key)
186188
}
@@ -329,6 +331,8 @@ type codec struct {
329331
generateRpcSamples bool
330332
// If true, the generator will set the internal builder's visibility to public (crate).
331333
internalBuilders bool
334+
// Overrides the default heuristically selected service for the package-level quickstart.
335+
quickstartServiceOverride string
332336
}
333337

334338
type systemParameter struct {

internal/sidekick/rust/codec_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,15 @@ func TestParseOptions(t *testing.T) {
324324
c.internalBuilders = true
325325
},
326326
},
327+
{
328+
Format: libconfig.SpecProtobuf,
329+
Options: map[string]string{
330+
"quickstart-service-override": "OverriddenService",
331+
},
332+
Update: func(c *codec) {
333+
c.quickstartServiceOverride = "OverriddenService"
334+
},
335+
},
327336
} {
328337
want, err := newCodec(test.Format, map[string]string{})
329338
if err != nil {

0 commit comments

Comments
 (0)