@@ -18,32 +18,30 @@ import (
1818 "context"
1919 "errors"
2020 "fmt"
21- "log/slog"
21+ "os"
22+ "path"
23+ "strconv"
24+ "strings"
25+ "time"
2226
2327 "github.com/googleapis/librarian/internal/config"
24- "github.com/googleapis/librarian/internal/librarian/internal/rust"
2528 "github.com/googleapis/librarian/internal/yaml"
2629 "github.com/urfave/cli/v3"
2730)
2831
2932var (
30- errUnsupportedLanguage = errors .New ("library creation is not supported for the specified language" )
31- errOutputFlagRequired = errors .New ("output flag is required when default.output is not set in librarian.yaml" )
32- errServiceConfigOrSpecRequired = errors .New ("both service-config and specification-source flags are required for creating a new library" )
33- errMissingNameFlag = errors .New ("name flag is required to create a new library" )
34- errNoYaml = errors .New ("unable to read librarian.yaml" )
33+ errUnsupportedLanguage = errors .New ("library creation is not supported for the specified language" )
34+ errOutputFlagRequired = errors .New ("output flag is required when default.output is not set in librarian.yaml" )
35+ errMissingLibraryName = errors .New ("must provide library name as argument to create a new library" )
36+ errNoYaml = errors .New ("unable to read librarian.yaml" )
3537)
3638
3739func createCommand () * cli.Command {
3840 return & cli.Command {
3941 Name : "create" ,
4042 Usage : "create a new client library" ,
41- UsageText : "librarian create --name <name> --specification-source < path> --service-config < path> " ,
43+ UsageText : "librarian create [library] --specification-source [ path] --service-config [ path] " ,
4244 Flags : []cli.Flag {
43- & cli.StringFlag {
44- Name : "name" ,
45- Usage : "library name" ,
46- },
4745 & cli.StringFlag {
4846 Name : "specification-source" ,
4947 Usage : "path to the specification source (e.g., google/cloud/secretmanager/v1)" ,
@@ -63,15 +61,15 @@ func createCommand() *cli.Command {
6361 },
6462 },
6563 Action : func (ctx context.Context , c * cli.Command ) error {
66- name := c .String ("name" )
64+ libraryName := c .Args ().First ()
65+ if libraryName == "" {
66+ return errMissingLibraryName
67+ }
6768 specSource := c .String ("specification-source" )
6869 serviceConfig := c .String ("service-config" )
6970 output := c .String ("output" )
7071 specFormat := c .String ("specification-format" )
71- if name == "" {
72- return errMissingNameFlag
73- }
74- return runCreate (ctx , name , specSource , serviceConfig , output , specFormat )
72+ return runCreate (ctx , libraryName , specSource , serviceConfig , output , specFormat )
7573 },
7674 }
7775}
@@ -80,36 +78,90 @@ func runCreate(ctx context.Context, name, specSource, serviceConfig, output, spe
8078 return runCreateWithGenerator (ctx , name , specSource , serviceConfig , output , specFormat , & Generate {})
8179}
8280
83- func runCreateWithGenerator (ctx context.Context , name , specSource , serviceConfig , output , specFormat string , gen Generator ) error {
81+ func runCreateWithGenerator (ctx context.Context , libraryName , specSource , serviceConfig , output , specFormat string , gen Generator ) error {
8482 cfg , err := yaml.Read [config.Config ](librarianConfigPath )
8583 if err != nil {
8684 return fmt .Errorf ("%w: %v" , errNoYaml , err )
8785 }
86+ // check for existing libraries, if it exists just run generate
87+ for _ , lib := range cfg .Libraries {
88+ if lib .Name == libraryName {
89+ return gen .Run (ctx , false , libraryName )
90+ }
91+ }
92+ specSource = deriveSpecSource (specSource , serviceConfig , cfg .Language )
93+ if output , err = deriveOutput (output , cfg , libraryName , specSource , cfg .Language ); err != nil {
94+ return err
95+ }
96+ if err := addLibraryToLibrarianConfig (cfg , libraryName , output , specSource , serviceConfig , specFormat ); err != nil {
97+ return err
98+ }
8899 switch cfg .Language {
89100 case "rust" :
90- for _ , lib := range cfg .Libraries {
91- if lib .Name == name {
92- return gen .Run (ctx , false , name )
93- }
94- }
101+ //TODO: add create logic
102+ return gen .Run (ctx , false , libraryName )
103+ default :
104+ return errUnsupportedLanguage
105+ }
106+ }
95107
96- // if we add support for creating veneers this check should be ignored
97- if serviceConfig == "" && specSource == "" {
98- return errServiceConfigOrSpecRequired
108+ func deriveSpecSource (specSource string , serviceConfig string , language string ) string {
109+ switch language {
110+ case "rust" :
111+ if specSource == "" && serviceConfig != "" {
112+ return path .Dir (serviceConfig )
99113 }
114+ }
115+ return specSource
116+ }
100117
118+ func deriveOutput (output string , cfg * config.Config , libraryName string , specSource string , language string ) (string , error ) {
119+ if output == "" && (cfg .Default == nil || cfg .Default .Output == "" ) {
120+ return "" , errOutputFlagRequired
121+ }
122+ switch language {
123+ case "rust" :
101124 if output == "" {
102125 if cfg .Default == nil || cfg .Default .Output == "" {
103- return errOutputFlagRequired
126+ return "" , errOutputFlagRequired
127+ }
128+ if specSource != "" {
129+ return defaultOutput (language , specSource , cfg .Default .Output ), nil
104130 }
105- output = rust .DefaultOutput (specSource , cfg .Default .Output )
131+ libOutputDir := strings .ReplaceAll (libraryName , "-" , "/" )
132+ return defaultOutput (language , libOutputDir , cfg .Default .Output ), nil
106133 }
107-
108- // TODO: port over sidekick rustGenerate logic to create a new librarian
109- slog .InfoContext (ctx , "Creating new Rust library" , "name" , name , "specSource" , specSource , "serviceConfig" , serviceConfig , "output" , output , "specFormat" , specFormat )
110- return nil
111134 default :
112- return errUnsupportedLanguage
135+ return defaultOutput (language , specSource , cfg .Default .Output ), nil
136+ }
137+
138+ return output , nil
139+ }
140+
141+ func addLibraryToLibrarianConfig (rootConfig * config.Config , name , output , specificationSource , serviceConfig , specificationFormat string ) error {
142+ lib := & config.Library {
143+ Name : name ,
144+ Output : output ,
145+ Version : "0.1.0" ,
146+ SpecificationFormat : specificationFormat ,
147+ CopyrightYear : strconv .Itoa (time .Now ().Year ()),
148+ }
149+ if serviceConfig != "" || specificationSource != "" {
150+ lib .Channels = []* config.Channel {
151+ {
152+ Path : specificationSource ,
153+ ServiceConfig : serviceConfig ,
154+ },
155+ }
156+ }
157+ rootConfig .Libraries = append (rootConfig .Libraries , lib )
158+ data , err := yaml .Marshal (rootConfig )
159+ if err != nil {
160+ return fmt .Errorf ("error marshaling librarian config: %w" , err )
113161 }
114162
163+ if err := os .WriteFile (librarianConfigPath , data , 0o644 ); err != nil {
164+ return fmt .Errorf ("error writing librarian.yaml: %w" , err )
165+ }
166+ return nil
115167}
0 commit comments