@@ -21,7 +21,6 @@ import (
2121 "io/fs"
2222 "os"
2323 "path/filepath"
24- "strings"
2524
2625 "github.com/googleapis/librarian/internal/config"
2726 "github.com/googleapis/librarian/internal/fetch"
@@ -79,6 +78,16 @@ func runGenerate(ctx context.Context, all bool, libraryName string) error {
7978}
8079
8180func generateAll (ctx context.Context , cfg * config.Config ) error {
81+ googleapisDir , err := fetchGoogleapisDir (ctx , cfg .Sources )
82+ if err != nil {
83+ return err
84+ }
85+
86+ libraries , err := deriveDefaultLibraries (cfg , googleapisDir )
87+ if err != nil {
88+ return err
89+ }
90+ cfg .Libraries = append (cfg .Libraries , libraries ... )
8291 for _ , lib := range cfg .Libraries {
8392 if err := generateLibrary (ctx , cfg , lib .Name ); err != nil {
8493 return err
@@ -87,6 +96,77 @@ func generateAll(ctx context.Context, cfg *config.Config) error {
8796 return nil
8897}
8998
99+ // deriveDefaultLibraries finds libraries for allowed channels that are not
100+ // explicitly configured in librarian.yaml.
101+ //
102+ // For each allowed channel without configuration, it derives default values
103+ // for the library name and output path. If the output directory exists, the
104+ // library is added for generation. Channels whose output directories do not
105+ // exist in the librarian.yaml but should be generated are returned.
106+ func deriveDefaultLibraries (cfg * config.Config , googleapisDir string ) ([]* config.Library , error ) {
107+ if cfg .Default == nil {
108+ return nil , nil
109+ }
110+
111+ configured := make (map [string ]bool )
112+ for _ , lib := range cfg .Libraries {
113+ for _ , ch := range lib .Channels {
114+ configured [ch .Path ] = true
115+ }
116+ }
117+
118+ var derived []* config.Library
119+ for channel := range serviceconfig .Allowlist {
120+ if configured [channel ] {
121+ continue
122+ }
123+ name := defaultLibraryName (cfg .Language , channel )
124+ output := defaultOutput (cfg .Language , channel , cfg .Default .Output )
125+ if ! dirExists (output ) {
126+ continue
127+ }
128+ sc , err := serviceconfig .Find (googleapisDir , channel )
129+ if err != nil {
130+ return nil , err
131+ }
132+ derived = append (derived , & config.Library {
133+ Name : name ,
134+ Output : output ,
135+ Channels : []* config.Channel {{
136+ Path : channel ,
137+ ServiceConfig : sc ,
138+ }},
139+ })
140+ }
141+ return derived , nil
142+ }
143+
144+ func defaultLibraryName (language , channel string ) string {
145+ switch language {
146+ case "rust" :
147+ return rust .DefaultLibraryName (channel )
148+ default :
149+ return channel
150+ }
151+ }
152+
153+ func defaultOutput (language , channel , defaultOut string ) string {
154+ switch language {
155+ case "rust" :
156+ return rust .DefaultOutput (channel , defaultOut )
157+ default :
158+ return defaultOut
159+ }
160+ }
161+
162+ func dirExists (path string ) bool {
163+ info , err := os .Stat (path )
164+ if err != nil {
165+ return false
166+ }
167+ return info .IsDir ()
168+ }
169+
90170func generateLibrary (ctx context.Context , cfg * config.Config , libraryName string ) error {
91171 googleapisDir , err := fetchGoogleapisDir (ctx , cfg .Sources )
92172 if err != nil {
@@ -98,7 +178,10 @@ func generateLibrary(ctx context.Context, cfg *config.Config, libraryName string
98178 fmt .Printf ("⊘ Skipping %s (skip_generate is set)\n " , lib .Name )
99179 return nil
100180 }
101- lib = prepareLibrary (cfg .Language , lib , cfg .Default )
181+ lib , err := prepareLibrary (cfg .Language , lib , cfg .Default )
182+ if err != nil {
183+ return err
184+ }
102185 for _ , api := range lib .Channels {
103186 if api .ServiceConfig == "" {
104187 serviceConfig , err := serviceconfig .Find (googleapisDir , api .Path )
@@ -117,26 +200,14 @@ func generateLibrary(ctx context.Context, cfg *config.Config, libraryName string
117200// prepareLibrary applies language-specific derivations and fills defaults.
118201// For Rust libraries without an explicit output path, it derives the output
119202// from the first channel path before applying defaults.
120- func prepareLibrary (language string , lib * config.Library , defaults * config.Default ) * config.Library {
121- // TODO(https://github.com/googleapis/librarian/issues/2966):
122- // refactor so that the switch statement logic is in one place
123- if language == "rust" && lib .Output == "" && len (lib .Channels ) > 0 {
124- lib .Output = deriveDefaultRustOutput (lib .Channels [0 ].Path , defaults .Output )
203+ func prepareLibrary (language string , lib * config.Library , defaults * config.Default ) (* config.Library , error ) {
204+ if lib .Output == "" {
205+ if len (lib .Channels ) == 0 {
206+ return nil , fmt .Errorf ("library %q has no channels, cannot determine default output" , lib .Name )
207+ }
208+ lib .Output = defaultOutput (language , lib .Channels [0 ].Path , defaults .Output )
125209 }
126- return fillDefaults (lib , defaults )
127- }
128-
129- // deriveDefaultRustOutput returns the output path for a Rust library. If the
130- // library has an explicit output path that differs from the default, it returns
131- // that path. Otherwise, it derives the output from the first channel path by
132- // stripping the "google/" prefix and joining with the default output. For
133- // example, the default output for google/cloud/secretmanager/v1 is
134- // src/generated/cloud/secretmanager/v1.
135- //
136- // TODO(https://github.com/googleapis/librarian/issues/2966): refactor and move
137- // to internal/rust package.
138- func deriveDefaultRustOutput (channel , defaultOutput string ) string {
139- return filepath .Join (defaultOutput , strings .TrimPrefix (channel , "google/" ))
210+ return fillDefaults (lib , defaults ), nil
140211}
141212
142213func generate (ctx context.Context , language string , library * config.Library , sources * config.Sources ) error {
@@ -147,7 +218,7 @@ func generate(ctx context.Context, language string, library *config.Library, sou
147218 case "rust" :
148219 keep := append (library .Keep , "Cargo.toml" )
149220 if err := cleanOutput (library .Output , keep ); err != nil {
150- return err
221+ return fmt . Errorf ( "library %s: %w" , library . Name , err )
151222 }
152223 err = rust .Generate (ctx , library , sources )
153224 default :
0 commit comments