diff --git a/internal/librariangen/languagecontainer/languagecontainer.go b/internal/librariangen/languagecontainer/languagecontainer.go new file mode 100644 index 0000000000..7cefea3568 --- /dev/null +++ b/internal/librariangen/languagecontainer/languagecontainer.go @@ -0,0 +1,119 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package languagecontainer defines LanguageContainer interface and +// the Run function to execute commands within the container. +// TODO(b/447404382): Move this package to the https://github.com/googleapis/librarian +// GitHub repository once the interface is finalized. +// This package must not have any Java-specific implementation. +package languagecontainer + +import ( + "context" + "log/slog" + + "cloud.google.com/java/internal/librariangen/message" +) + +// GenerateCommandContext holds the context (the file system paths) for +// the generate command. The createGenerateContext function creates +// an instance of this by reading the command line flags and the +// default values. +type GenerateCommandContext struct { +} +type GenerateCommandEnv struct { + GenerateContext *GenerateCommandContext + GenerateRequest *message.GenerateRequest +} + +type ConfigureCommandContext struct { +} + +type ConfigureCommandEnv struct { + ConfigureContext *ConfigureCommandContext + ConfigureRequest *message.ConfigureRequest +} + +// LanguageContainer defines the interface for language-specific container operations. +type LanguageContainer interface { + Generate(context.Context, *GenerateCommandEnv) error + Configure(context.Context, *ConfigureCommandEnv) (*message.ConfigureResponse, error) + // Other container functions like ReleaseInit and Build would also be part of the interface. +} + +// Run would accept an implementation of the LanguageContainer interface. +func Run(args []string, container LanguageContainer) int { + // Logic to parse args and call the appropriate method on the container. + // For example, if args[1] is "generate": + // request := ... // unmarshal the request from the expected location + // err := container.Generate(context.Background(), request) + // ... + if len(args) < 1 { + panic("args must not be empty") + } + switch args[0] { + case "generate": + env := createGenerateEnv(args[1:]) + err := container.Generate(context.Background(), env) + if err != nil { + // TODO: Save it as a response file. + panic(err) + } + return 0 + case "configure": + env := createConfigureEnv(args[1:]) + resp, err := container.Configure(context.Background(), env) + if err != nil { + panic(err) + } + // TODO: Save it as a response file. + _ = resp + return 0 + case "release-init": + slog.Warn("librariangen: release-init command is not yet implemented") + return 1 + case "build": + slog.Warn("librariangen: build command is not yet implemented") + return 1 + default: + slog.Error("librariangen: unknown command: %s (with flags %v)", args[0], args) + return 1 + } + return 0 +} + +// https://github.com/googleapis/librarian/blob/main/doc/language-onboarding.md#generate +func createGenerateEnv(args []string) *GenerateCommandEnv { + generateContext := createGenerateCommandContext(args) + return &GenerateCommandEnv{ + GenerateContext: generateContext, + } +} + +func createGenerateCommandContext(args []string) *GenerateCommandContext { + // TODO: Parse args and create the context. + return &GenerateCommandContext{} +} + +func createConfigureCommandContext(args []string) *ConfigureCommandContext { + // TODO: Parse args and create the context. + return &ConfigureCommandContext{} +} + +func createConfigureEnv(args []string) *ConfigureCommandEnv { + configureContext := createConfigureCommandContext(args) + return &ConfigureCommandEnv{ + ConfigureContext: configureContext, + } +} diff --git a/internal/librariangen/main.go b/internal/librariangen/main.go index 40e2c6b7b7..a36d007db5 100644 --- a/internal/librariangen/main.go +++ b/internal/librariangen/main.go @@ -16,32 +16,42 @@ package main import ( "context" - "errors" - "fmt" "log/slog" "os" - "strings" + + "cloud.google.com/java/internal/librariangen/languagecontainer" + "cloud.google.com/java/internal/librariangen/message" ) const version = "0.1.0" -// main is the entrypoint for the librariangen CLI. -func main() { - os.Exit(runCLI(os.Args)) +// javaContainer implements the LanguageContainer interface for Java. +type javaContainer struct{} + +func (c *javaContainer) Generate(context.Context, *languagecontainer.GenerateCommandEnv) error { + // Java-specific implementation for the "generate" command. + slog.Warn("librariangen: generate command is not yet implemented") + + return nil } -func runCLI(args []string) int { +func (c *javaContainer) Configure(ctx context.Context, + request *languagecontainer.ConfigureCommandEnv) (*message.ConfigureResponse, error) { + // Java-specific implementation for the "configure" command. + slog.Warn("librariangen: configure command is not yet implemented") + return nil, nil +} + +// main is the entrypoint for the librariangen CLI. +func main() { logLevel := parseLogLevel(os.Getenv("GOOGLE_SDK_JAVA_LOGGING_LEVEL")) slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ Level: logLevel, }))) + args := os.Args slog.Info("librariangen: invoked", "args", args) - if err := run(context.Background(), args[1:]); err != nil { - slog.Error("librariangen: failed", "error", err) - return 1 - } - slog.Info("librariangen: finished successfully") - return 0 + container := javaContainer{} + os.Exit(languagecontainer.Run(os.Args, &container)) } func parseLogLevel(logLevelEnv string) slog.Level { @@ -54,42 +64,3 @@ func parseLogLevel(logLevelEnv string) slog.Level { return slog.LevelInfo } } - -// run executes the appropriate command based on the CLI's invocation arguments. -// The idiomatic structure is `librariangen [command] [flags]`. -func run(ctx context.Context, args []string) error { - if len(args) < 1 { - return errors.New("librariangen: expected a command") - } - - // The --version flag is a special case and not a command. - if args[0] == "--version" { - fmt.Println(version) - return nil - } - - cmd := args[0] - flags := args[1:] - - if strings.HasPrefix(cmd, "-") { - return fmt.Errorf("librariangen: command cannot be a flag: %s", cmd) - } - - switch cmd { - case "generate": - slog.Warn("librariangen: generate command is not yet implemented") - return nil - case "release-init": - slog.Warn("librariangen: release-init command is not yet implemented") - return nil - case "configure": - slog.Warn("librariangen: configure command is not yet implemented") - return nil - case "build": - slog.Warn("librariangen: build command is not yet implemented") - return nil - default: - return fmt.Errorf("librariangen: unknown command: %s (with flags %s)", cmd, flags) - } - -} diff --git a/internal/librariangen/message/message.go b/internal/librariangen/message/message.go new file mode 100644 index 0000000000..f464e493f1 --- /dev/null +++ b/internal/librariangen/message/message.go @@ -0,0 +1,42 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package message defines data types which the Librarian CLI and language +// containers exchange. +// There shouldn't be CLI-specific data type or language container-specific +// data types in this package. +// TODO(b/447404382): Move this package to the https://github.com/googleapis/librarian +// GitHub repository once the interface is finalized. +// This package must not have any Java-specific implementation. +package message + +// GenerateRequest is the JSON message sent to the language container by +// the Librarian CLI when the "generate" command is invoked. +type GenerateRequest struct { +} + +// GenerateResponse is the JSON message sent back to the Librarian CLI +// by the language container after processing the "generate" command. +type GenerateResponse struct { +} + +// ConfigureRequest is the JSON message sent to the language container by +// the Librarian CLI when the "configure" command is invoked. +type ConfigureRequest struct { +} + +// ConfigureResponse is the JSON message sent back to the Librarian CLI +// by the language container after processing the "configure" command. +type ConfigureResponse struct { +}