Skip to content

Commit 78db342

Browse files
authored
refactor(librarian/rust): reimplement Rust library .repo-metadata.json generation (#4340)
Use common internal/repometadata package rather than rust-specific template For #3606
1 parent 54d4436 commit 78db342

10 files changed

Lines changed: 137 additions & 55 deletions

File tree

internal/librarian/fake_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func TestGenerateLibraries(t *testing.T) {
3838

3939
tmpDir := t.TempDir()
4040
t.Chdir(tmpDir)
41-
if err := generateLibraries(t.Context(), cfg, []*config.Library{library}, "", nil); err != nil {
41+
if err := generateLibraries(t.Context(), cfg, []*config.Library{library}, "googleapis", nil); err != nil {
4242
t.Fatal(err)
4343
}
4444

internal/librarian/generate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,13 @@ func generateLibraries(ctx context.Context, cfg *config.Config, libraries []*con
195195
case config.LanguageDart:
196196
return dart.GenerateLibraries(ctx, libraries, src)
197197
case config.LanguagePython:
198-
return python.GenerateLibraries(ctx, cfg, libraries, googleapisDir)
198+
return python.GenerateLibraries(ctx, cfg, libraries, src)
199199
case config.LanguageGo:
200200
return golang.GenerateLibraries(ctx, libraries, googleapisDir)
201201
case config.LanguageJava:
202202
return java.GenerateLibraries(ctx, libraries, googleapisDir)
203203
case config.LanguageRust:
204-
return rust.GenerateLibraries(ctx, libraries, src)
204+
return rust.GenerateLibraries(ctx, cfg, libraries, src)
205205
default:
206206
return fmt.Errorf("language %q does not support generation", cfg.Language)
207207
}

internal/librarian/python/generate.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/googleapis/librarian/internal/filesystem"
2929
"github.com/googleapis/librarian/internal/repometadata"
3030
"github.com/googleapis/librarian/internal/serviceconfig"
31+
sidekickconfig "github.com/googleapis/librarian/internal/sidekick/config"
3132
)
3233

3334
const (
@@ -36,17 +37,17 @@ const (
3637
)
3738

3839
// GenerateLibraries generates all the given libraries in sequence.
39-
func GenerateLibraries(ctx context.Context, config *config.Config, libraries []*config.Library, googleapisDir string) error {
40+
func GenerateLibraries(ctx context.Context, config *config.Config, libraries []*config.Library, sources *sidekickconfig.Sources) error {
4041
for _, library := range libraries {
41-
if err := generate(ctx, config, library, googleapisDir); err != nil {
42+
if err := generate(ctx, config, library, sources); err != nil {
4243
return err
4344
}
4445
}
4546
return nil
4647
}
4748

4849
// generate generates a Python client library.
49-
func generate(ctx context.Context, config *config.Config, library *config.Library, googleapisDir string) error {
50+
func generate(ctx context.Context, config *config.Config, library *config.Library, sources *sidekickconfig.Sources) error {
5051
// If the library has no APIs, there's nothing to do.
5152
if len(library.APIs) == 0 {
5253
return nil
@@ -69,15 +70,15 @@ func generate(ctx context.Context, config *config.Config, library *config.Librar
6970
// and pass it down.
7071
repoRoot := filepath.Dir(filepath.Dir(outdir))
7172
for _, api := range library.APIs {
72-
if err := generateAPI(ctx, api, library, googleapisDir, repoRoot); err != nil {
73+
if err := generateAPI(ctx, api, library, sources.Googleapis, repoRoot); err != nil {
7374
return fmt.Errorf("failed to generate api %q: %w", api.Path, err)
7475
}
7576
}
7677

7778
// Construct the repo metadata in memory, then write it to disk. This has
7879
// to be before post-processing, as the data in .repo-metadata.json is used
7980
// by the post-processor, primarily for documentation.
80-
repoMetadata, err := createRepoMetadata(config, library, googleapisDir)
81+
repoMetadata, err := createRepoMetadata(config, library, sources)
8182
if err != nil {
8283
return err
8384
}
@@ -106,13 +107,13 @@ func generate(ctx context.Context, config *config.Config, library *config.Librar
106107

107108
// createRepoMetadata creates (in memory, not on disk) a RepoMetadata suitable
108109
// for the given library.
109-
func createRepoMetadata(cfg *config.Config, library *config.Library, googleapisDir string) (*repometadata.RepoMetadata, error) {
110+
func createRepoMetadata(cfg *config.Config, library *config.Library, sources *sidekickconfig.Sources) (*repometadata.RepoMetadata, error) {
110111
// Just to avoid lots of checks for library.Python being nil.
111112
packageOptions := library.Python
112113
if packageOptions == nil {
113114
packageOptions = &config.PythonPackage{}
114115
}
115-
repoMetadata, err := repometadata.FromLibrary(cfg, library, googleapisDir)
116+
repoMetadata, err := repometadata.FromLibrary(cfg, library, sources)
116117
if err != nil {
117118
return nil, err
118119
}

internal/librarian/python/generate_test.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/google/go-cmp/cmp"
2828
"github.com/googleapis/librarian/internal/config"
2929
"github.com/googleapis/librarian/internal/repometadata"
30+
sidekickconfig "github.com/googleapis/librarian/internal/sidekick/config"
3031
"github.com/googleapis/librarian/internal/testhelper"
3132
)
3233

@@ -608,7 +609,8 @@ func TestGenerateLibraries(t *testing.T) {
608609
for _, library := range libraries {
609610
library.Output = filepath.Join(repoRoot, "packages", library.Name)
610611
}
611-
if err := GenerateLibraries(t.Context(), cfg, libraries, googleapisDir); err != nil {
612+
sources := &sidekickconfig.Sources{Googleapis: googleapisDir}
613+
if err := GenerateLibraries(t.Context(), cfg, libraries, sources); err != nil {
612614
t.Fatal(err)
613615
}
614616
for _, library := range libraries {
@@ -649,7 +651,8 @@ func TestGenerateLibraries_Error(t *testing.T) {
649651
},
650652
},
651653
}
652-
gotErr := GenerateLibraries(t.Context(), cfg, libraries, googleapisDir)
654+
sources := &sidekickconfig.Sources{Googleapis: googleapisDir}
655+
gotErr := GenerateLibraries(t.Context(), cfg, libraries, sources)
653656
wantErr := os.ErrPermission
654657
if !errors.Is(gotErr, wantErr) {
655658
t.Errorf("GenerateLibraries error = %v, wantErr %v", gotErr, wantErr)
@@ -670,7 +673,9 @@ func TestGenerate_NoAPIs(t *testing.T) {
670673
Name: "no-apis",
671674
Output: filepath.Join(repoRoot, "packages", "will-not-be-created"),
672675
}
673-
if err := generate(t.Context(), cfg, library, googleapisDir); err != nil {
676+
677+
sources := &sidekickconfig.Sources{Googleapis: googleapisDir}
678+
if err := generate(t.Context(), cfg, library, sources); err != nil {
674679
t.Fatal(err)
675680
}
676681
// Validate that we haven't got as far as creating the output directory.
@@ -722,7 +727,9 @@ func TestGenerate(t *testing.T) {
722727
},
723728
},
724729
}
725-
if err := generate(t.Context(), cfg, library, googleapisDir); err != nil {
730+
731+
sources := &sidekickconfig.Sources{Googleapis: googleapisDir}
732+
if err := generate(t.Context(), cfg, library, sources); err != nil {
726733
t.Fatal(err)
727734
}
728735
gotMetadata, err := repometadata.Read(outdir)
@@ -930,7 +937,8 @@ func TestCreateRepoMetadata(t *testing.T) {
930937
Language: config.LanguagePython,
931938
Repo: "googleapis/google-cloud-python",
932939
}
933-
got, err := createRepoMetadata(cfg, test.library, googleapisDir)
940+
sources := &sidekickconfig.Sources{Googleapis: googleapisDir}
941+
got, err := createRepoMetadata(cfg, test.library, sources)
934942
if err != nil {
935943
t.Fatal(err)
936944
}
@@ -953,7 +961,8 @@ func TestCreateRepoMetadata_Error(t *testing.T) {
953961
}
954962
// We don't check what the error is here; there's only one place it can
955963
// come, and it's not an error we create ourselves.
956-
_, err := createRepoMetadata(cfg, library, googleapisDir)
964+
sources := &sidekickconfig.Sources{Googleapis: googleapisDir}
965+
_, err := createRepoMetadata(cfg, library, sources)
957966
if err == nil {
958967
t.Error("expected error, got nil")
959968
}

internal/librarian/rust/generate.go

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
"github.com/googleapis/librarian/internal/command"
2626
"github.com/googleapis/librarian/internal/config"
27+
"github.com/googleapis/librarian/internal/repometadata"
2728
sidekickconfig "github.com/googleapis/librarian/internal/sidekick/config"
2829
"github.com/googleapis/librarian/internal/sidekick/parser"
2930
sidekickrust "github.com/googleapis/librarian/internal/sidekick/rust"
@@ -42,19 +43,19 @@ func IsVeneer(lib *config.Library) bool {
4243
}
4344

4445
// GenerateLibraries generates all the given libraries in parallel.
45-
func GenerateLibraries(ctx context.Context, libraries []*config.Library, sources *sidekickconfig.Sources) error {
46+
func GenerateLibraries(ctx context.Context, config *config.Config, libraries []*config.Library, sources *sidekickconfig.Sources) error {
4647
// Generate all libraries in parallel.
4748
g, gctx := errgroup.WithContext(ctx)
4849
for _, lib := range libraries {
4950
g.Go(func() error {
50-
return generate(gctx, lib, sources)
51+
return generate(gctx, config, lib, sources)
5152
})
5253
}
5354
return g.Wait()
5455
}
5556

5657
// generate generates a Rust client library.
57-
func generate(ctx context.Context, library *config.Library, sources *sidekickconfig.Sources) error {
58+
func generate(ctx context.Context, cfg *config.Config, library *config.Library, sources *sidekickconfig.Sources) error {
5859
if IsVeneer(library) {
5960
return generateVeneer(ctx, library, sources)
6061
}
@@ -85,12 +86,45 @@ func generate(ctx context.Context, library *config.Library, sources *sidekickcon
8586
if err := sidekickrust.Generate(ctx, model, library.Output, modelConfig); err != nil {
8687
return err
8788
}
89+
if len(model.Services) > 0 {
90+
repoMetadata, err := createRepoMetadata(cfg, library, sources)
91+
if err != nil {
92+
return err
93+
}
94+
if err := repoMetadata.Write(library.Output); err != nil {
95+
return err
96+
}
97+
}
8898
if !exists {
8999
validate(ctx, library.Output)
90100
}
91101
return nil
92102
}
93103

104+
func createRepoMetadata(cfg *config.Config, library *config.Library, sources *sidekickconfig.Sources) (*repometadata.RepoMetadata, error) {
105+
metadata, err := repometadata.FromLibrary(cfg, library, sources)
106+
if err != nil {
107+
return nil, err
108+
}
109+
// Overwrite fields to match current behavior
110+
metadata.APIDescription = ""
111+
metadata.DistributionName = "google-cloud-rust"
112+
metadata.IssueTracker = ""
113+
metadata.LibraryType = "GAPIC_AUTO"
114+
metadata.Name = ""
115+
if !strings.HasSuffix(metadata.NamePretty, " API") {
116+
// Reverse the cleaning done in FromLibrary.
117+
metadata.NamePretty = metadata.NamePretty + " API"
118+
}
119+
metadata.ProductDocumentation = ""
120+
121+
// Set fields that are not set by FromLibrary
122+
metadata.ClientDocumentation = fmt.Sprintf("https://docs.rs/%s/latest", library.Name)
123+
metadata.Repo = "googleapis/google-cloud-rust"
124+
125+
return metadata, nil
126+
}
127+
94128
// UpdateWorkspace updates dependencies for the entire Rust workspace.
95129
func UpdateWorkspace(ctx context.Context) error {
96130
return command.Run(ctx, "cargo", "update", "--workspace")

internal/librarian/rust/generate_test.go

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
"github.com/google/go-cmp/cmp"
2626
"github.com/googleapis/librarian/internal/config"
27+
"github.com/googleapis/librarian/internal/repometadata"
2728
sidekickconfig "github.com/googleapis/librarian/internal/sidekick/config"
2829
"github.com/googleapis/librarian/internal/testhelper"
2930
)
@@ -70,7 +71,7 @@ func TestGenerateVeneer(t *testing.T) {
7071
sources := &sidekickconfig.Sources{
7172
Googleapis: googleapisDir,
7273
}
73-
if err := generate(t.Context(), library, sources); err != nil {
74+
if err := generate(t.Context(), &config.Config{Language: "rust", Repo: "google-cloud-rust"}, library, sources); err != nil {
7475
t.Fatal(err)
7576
}
7677

@@ -175,7 +176,7 @@ func TestGenerateVeneerNoModules(t *testing.T) {
175176
sources := &sidekickconfig.Sources{
176177
Googleapis: googleapisDir,
177178
}
178-
if err := generate(t.Context(), library, sources); err != nil {
179+
if err := generate(t.Context(), &config.Config{Language: "rust", Repo: "google-cloud-rust"}, library, sources); err != nil {
179180
t.Fatal(err)
180181
}
181182

@@ -323,7 +324,8 @@ func TestGenerateLibraries(t *testing.T) {
323324
library.Output = filepath.Join("generated", library.Name)
324325
}
325326

326-
if err := GenerateLibraries(t.Context(), libraries, sources); err != nil {
327+
cfg := &config.Config{Language: "rust", Repo: "google-cloud-rust"}
328+
if err := GenerateLibraries(t.Context(), cfg, libraries, sources); err != nil {
327329
t.Fatal(err)
328330
}
329331
// Just check that a Cargo.toml has been created for each library.
@@ -381,7 +383,8 @@ func TestGenerateLibraries_Error(t *testing.T) {
381383
}
382384
t.Chdir(workspaceDir)
383385

384-
gotErr := GenerateLibraries(t.Context(), libraries, sources)
386+
cfg := &config.Config{Language: "rust", Repo: "google-cloud-rust"}
387+
gotErr := GenerateLibraries(t.Context(), cfg, libraries, sources)
385388
wantErrMessage := "unknown specification format \"invalid\""
386389
if gotErr == nil {
387390
t.Fatalf("expected error with message %s", wantErrMessage)
@@ -472,7 +475,7 @@ func TestGenerate(t *testing.T) {
472475
sources := &sidekickconfig.Sources{
473476
Googleapis: googleapisDir,
474477
}
475-
if err := generate(t.Context(), library, sources); err != nil {
478+
if err := generate(t.Context(), &config.Config{Language: "rust", Repo: "google-cloud-rust"}, library, sources); err != nil {
476479
t.Fatal(err)
477480
}
478481

@@ -485,6 +488,7 @@ func TestGenerate(t *testing.T) {
485488
{filepath.Join(outDir, "README.md"), "# Google Cloud Client Libraries for Rust - Secret Manager API"},
486489
{filepath.Join(outDir, "src", "lib.rs"), "pub mod model;"},
487490
{filepath.Join(outDir, "src", "lib.rs"), "pub mod client;"},
491+
{filepath.Join(outDir, ".repo-metadata.json"), "secretmanager.googleapis.com"},
488492
} {
489493
t.Run(check.path, func(t *testing.T) {
490494
if _, err := os.Stat(check.path); err != nil {
@@ -610,3 +614,51 @@ func TestFindModuleByOutput(t *testing.T) {
610614
})
611615
}
612616
}
617+
func TestCreateRepoMetadata(t *testing.T) {
618+
googleapisDir, err := filepath.Abs("../../testdata/googleapis")
619+
if err != nil {
620+
t.Fatal(err)
621+
}
622+
cfg := &config.Config{
623+
Language: config.LanguageRust,
624+
Repo: "googleapis/google-cloud-rust",
625+
}
626+
library := &config.Library{
627+
Name: "google-cloud-secretmanager-v1",
628+
Version: "0.1.0",
629+
ReleaseLevel: "preview",
630+
APIs: []*config.API{
631+
{
632+
Path: "google/cloud/secretmanager/v1",
633+
},
634+
},
635+
}
636+
sources := &sidekickconfig.Sources{
637+
Googleapis: googleapisDir,
638+
}
639+
640+
got, err := createRepoMetadata(cfg, library, sources)
641+
if err != nil {
642+
t.Fatal(err)
643+
}
644+
645+
want := &repometadata.RepoMetadata{
646+
Name: "",
647+
NamePretty: "Secret Manager API",
648+
ProductDocumentation: "",
649+
ClientDocumentation: "https://docs.rs/google-cloud-secretmanager-v1/latest",
650+
IssueTracker: "",
651+
ReleaseLevel: "preview",
652+
Language: config.LanguageRust,
653+
Repo: "googleapis/google-cloud-rust",
654+
DistributionName: "google-cloud-rust",
655+
APIID: "secretmanager.googleapis.com",
656+
APIShortname: "secretmanager",
657+
APIDescription: "",
658+
LibraryType: "GAPIC_AUTO",
659+
}
660+
661+
if diff := cmp.Diff(want, got); diff != "" {
662+
t.Errorf("mismatch (-want +got):\n%s", diff)
663+
}
664+
}

internal/repometadata/repometadata.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
"github.com/googleapis/librarian/internal/config"
2828
"github.com/googleapis/librarian/internal/serviceconfig"
29+
sidekickconfig "github.com/googleapis/librarian/internal/sidekick/config"
2930
)
3031

3132
const (
@@ -109,15 +110,19 @@ type RepoMetadata struct {
109110
//
110111
// Any other fields required by the caller's language should be populated by the
111112
// caller before writing to disk.
112-
func FromLibrary(config *config.Config, library *config.Library, googleapisDir string) (*RepoMetadata, error) {
113+
func FromLibrary(config *config.Config, library *config.Library, sources *sidekickconfig.Sources) (*RepoMetadata, error) {
113114
// TODO(https://github.com/googleapis/librarian/issues/3146):
114115
// Compute the default version, potentially with an override, instead of
115116
// taking it as a parameter.
116117
if len(library.APIs) == 0 {
117118
return nil, fmt.Errorf("failed to generate metadata for %s: %w", library.Name, errNoAPIs)
118119
}
119120
firstAPIPath := library.APIs[0].Path
120-
api, err := serviceconfig.Find(googleapisDir, firstAPIPath, config.Language)
121+
root := sources.Googleapis
122+
if firstAPIPath == "schema/google/showcase/v1beta1" {
123+
root = sources.Showcase
124+
}
125+
api, err := serviceconfig.Find(root, firstAPIPath, config.Language)
121126
if err != nil {
122127
return nil, fmt.Errorf("failed to find API for path %s: %w", firstAPIPath, err)
123128
}

0 commit comments

Comments
 (0)