feat: json output formatter

This commit is contained in:
2025-07-20 14:15:20 +02:00
parent 7d561ea6ea
commit 0b0f0c9f0a
8 changed files with 60 additions and 22 deletions

View File

@ -4,11 +4,3 @@ type AppConfig struct {
OutputVerbose bool OutputVerbose bool
OutputMode string OutputMode string
} }
type OutputMode string
const (
Go OutputMode = "go"
Json OutputMode = "json"
Yaml OutputMode = "yaml"
)

View File

@ -1,5 +0,0 @@
package cmd
type OutputProvider interface {
Convert(item any) (string, error)
}

View File

@ -31,12 +31,12 @@ func initRootCmd() {
var appConfig AppConfig var appConfig AppConfig
rootCmd.PersistentFlags().BoolVarP(&appConfig.OutputVerbose, "verbose", "v", false, "Enable verbose output") rootCmd.PersistentFlags().BoolVarP(&appConfig.OutputVerbose, "verbose", "v", false, "Enable verbose output")
rootCmd.PersistentFlags().StringVarP(&appConfig.OutputMode, "output", "o", string(Json), "Set output format") rootCmd.PersistentFlags().StringVarP(&appConfig.OutputMode, "output", "o", "json", "Set output format")
logger := jlog.New(slog.LevelDebug) logger := jlog.New(slog.LevelDebug)
ctx := jlog.ContextWith(context.Background(), logger) ctx := jlog.ContextWith(context.Background(), logger)
logger.Debug("Register verb commands") logger.Debug("Register verb commands")
rootCmd.AddCommand(getVerbs(ctx)...) rootCmd.AddCommand(getVerbs(ctx, appConfig)...)
logger.Debug("Verb commands registered successfully") logger.Debug("Verb commands registered successfully")
} }

View File

@ -2,8 +2,8 @@ package cmd
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"io"
"log/slog" "log/slog"
"os" "os"
"slices" "slices"
@ -11,6 +11,7 @@ import (
"git.bissendorf.co/bissendorf/unifood/m/v2/core/interfaces" "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/interfaces/params"
"git.bissendorf.co/bissendorf/unifood/m/v2/core/output"
"git.bissendorf.co/bissendorf/unifood/m/v2/core/services/jlog" "git.bissendorf.co/bissendorf/unifood/m/v2/core/services/jlog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -19,32 +20,47 @@ type VerbItem struct {
Name interfaces.Verb Name interfaces.Verb
Aliases []string Aliases []string
Description string Description string
RunFn func(ctx context.Context, handler interfaces.ResourceHandler, params params.Container) error RunFn func(ctx context.Context, config AppConfig, handler interfaces.ResourceHandler, params params.Container) error
} }
var verbs = []VerbItem{ var verbs = []VerbItem{
{ {
Name: interfaces.VerbGet, Name: interfaces.VerbGet,
Description: "Retrieve resource information", Description: "Retrieve resource information",
RunFn: func(ctx context.Context, handler interfaces.ResourceHandler, params params.Container) error { RunFn: func(ctx context.Context, config AppConfig, handler interfaces.ResourceHandler, params params.Container) error {
h, ok := handler.(interfaces.GetHandler) h, ok := handler.(interfaces.GetHandler)
if !ok { if !ok {
return fmt.Errorf("resource does not support GET") return fmt.Errorf("resource does not support GET")
} }
// Get item
item, err := h.Get(ctx, params) item, err := h.Get(ctx, params)
if err != nil { if err != nil {
return fmt.Errorf("retrieving item failed: %w", err) return fmt.Errorf("retrieving item failed: %w", err)
} }
json.NewEncoder(os.Stdout).Encode(item) formatterName := strings.ToLower(config.OutputMode)
formatter, exists := output.Formatters[formatterName]
if !exists {
return fmt.Errorf("could not find output formatter '%s'", formatterName)
}
// Format and output
formatted, err := formatter.Format(item)
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 return nil
}, },
}, },
} }
func getVerbs(ctx context.Context) (commands []*cobra.Command) { func getVerbs(ctx context.Context, config AppConfig) (commands []*cobra.Command) {
logger := jlog.FromContext(ctx) logger := jlog.FromContext(ctx)
for _, v := range verbs { for _, v := range verbs {
@ -69,7 +85,7 @@ func getVerbs(ctx context.Context) (commands []*cobra.Command) {
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
logger := jlog.New(slog.LevelInfo) logger := jlog.New(slog.LevelInfo)
ctx := jlog.ContextWith(context.Background(), logger) ctx := jlog.ContextWith(context.Background(), logger)
err := v.RunFn(ctx, r.Handler, params) err := v.RunFn(ctx, config, r.Handler, params)
if err != nil { if err != nil {
logger.ErrorContext(ctx, fmt.Sprintf("%s %s failed", strings.ToUpper(string(v.Name)), r.Name), "error", err.Error()) logger.ErrorContext(ctx, fmt.Sprintf("%s %s failed", strings.ToUpper(string(v.Name)), r.Name), "error", err.Error())
os.Exit(1) os.Exit(1)

View File

@ -0,0 +1,7 @@
package interfaces
import "io"
type Formatter interface {
Format(object any) (io.Reader, error)
}

12
core/output/formatter.go Normal file
View File

@ -0,0 +1,12 @@
package output
import (
"git.bissendorf.co/bissendorf/unifood/m/v2/core/interfaces"
)
var Formatters = map[string]interfaces.Formatter{
"json": &JsonFormatter{},
"go": nil,
"table": nil,
"xml": nil,
}

15
core/output/json.go Normal file
View File

@ -0,0 +1,15 @@
package output
import (
"bytes"
"encoding/json"
"io"
)
type JsonFormatter struct{}
func (f *JsonFormatter) Format(object any) (io.Reader, error) {
var buffer = make([]byte, 0, 1024)
outputBuffer := bytes.NewBuffer(buffer)
return outputBuffer, json.NewEncoder(outputBuffer).Encode(object)
}

3
go.mod
View File

@ -2,8 +2,9 @@ module git.bissendorf.co/bissendorf/unifood/m/v2
go 1.24.0 go 1.24.0
require github.com/spf13/cobra v1.9.1
require ( require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/cobra v1.9.1 // indirect
github.com/spf13/pflag v1.0.7 // indirect github.com/spf13/pflag v1.0.7 // indirect
) )