package cmd import ( "context" "fmt" "io" "log/slog" "os" "slices" "strings" "time" "git.bissendorf.co/bissendorf/unifood/m/v2/core/interfaces" "git.bissendorf.co/bissendorf/unifood/m/v2/core/interfaces/params" "git.bissendorf.co/bissendorf/unifood/m/v2/core/output" "git.bissendorf.co/bissendorf/unifood/m/v2/core/services/jlog" "github.com/spf13/cobra" ) type VerbItem struct { Name interfaces.Verb Aliases []string Description string RunFn func(ctx context.Context, config *AppConfig, handler interfaces.ResourceHandler, params params.Container, name string) error } var verbs = []VerbItem{ { Name: interfaces.VerbGet, Description: "Retrieve a list of resources", RunFn: func(ctx context.Context, config *AppConfig, handler interfaces.ResourceHandler, params params.Container, name string) error { h, ok := handler.(interfaces.GetHandler) if !ok { return fmt.Errorf("resource does not support GET") } // Get items items, err := h.Get(ctx, name, params) if err != nil { return fmt.Errorf("retrieving item failed: %w", err) } if config.OutputOrderReverse { slices.Reverse(items.Items) } formatterName := strings.ToLower(config.OutputFormatter) formatter, exists := output.Formatters[formatterName] if !exists { return fmt.Errorf("could not find output formatter '%s'", formatterName) } // Format and output formatted, err := formatter.Format(items) if err != nil { return fmt.Errorf("failed to format output: %w", err) } _, err = io.Copy(os.Stdout, formatted) if err != nil { return fmt.Errorf("failed to write output: %w", err) } return nil }, }, } func getVerbs(ctx context.Context, config *AppConfig) (commands []*cobra.Command) { for _, v := range verbs { verbCommand := &cobra.Command{ Use: strings.ToLower(string(v.Name)), Aliases: v.Aliases, Short: v.Description, } // Add all resources that can handle this verb for _, r := range availableResources { if !slices.Contains(r.Verbs, v.Name) { continue } var params params.Container resourceCommand := &cobra.Command{ Use: r.Name, Aliases: r.Aliases, Short: r.Description, Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { // Configure log var logLevel = slog.LevelWarn if config.OutputVerbose { logLevel = slog.LevelDebug } logger := jlog.New(logLevel) ctx := jlog.ContextWith(context.Background(), logger) ctx, cancel := context.WithTimeout(ctx, time.Duration(config.RequestTimeoutSeconds)*time.Second) defer cancel() // Print config if config.PrintConfig { logger.WarnContext(ctx, "Printing app config", slog.Any("config", config), slog.Any("parameters", params.ToMap())) } var name string if len(args) > 0 { name = args[0] } err := v.RunFn(ctx, config, r.Handler, params, name) if err != nil { logger.ErrorContext(ctx, fmt.Sprintf("%s %s failed", strings.ToUpper(string(v.Name)), r.Name), "error", err.Error()) os.Exit(1) } }, } // Register parameters for _, param := range r.Handler.GetParametersForVerb(v.Name) { resourceCommand.Flags().StringVarP( params.Register(param.Name, param.ParseFunc), param.Name, param.ShortHand, param.DefaultFunc(), param.Description, ) } verbCommand.AddCommand(resourceCommand) } commands = append(commands, verbCommand) } return }