184 lines
4.2 KiB
Go
184 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
|
|
"git.loyso.art/frx/kurious/internal/common/client/sravni"
|
|
"git.loyso.art/frx/kurious/internal/common/config"
|
|
"git.loyso.art/frx/kurious/internal/common/errors"
|
|
"git.loyso.art/frx/kurious/internal/kurious/adapters"
|
|
|
|
"github.com/teris-io/cli"
|
|
)
|
|
|
|
var (
|
|
limitOption = cli.NewOption("limit", "Limits amount of items to return").WithType(cli.TypeInt)
|
|
offsetOption = cli.NewOption("offset", "Offsets items to return").WithType(cli.TypeInt)
|
|
debugOption = cli.NewOption("verbose", "Enables debug logging").WithChar('v').WithType(cli.TypeBool)
|
|
jsonOption = cli.NewOption("json", "Sets output as json").WithType(cli.TypeBool)
|
|
)
|
|
|
|
type actionWrapper func(next cli.Action) cli.Action
|
|
|
|
func buildCLICommand(f func() cli.Command) cliCommand {
|
|
return makeCLICommand(f(), actionWrapperParseConfig)
|
|
}
|
|
|
|
func makeCLICommand(cmd cli.Command, wrappers ...actionWrapper) cliCommand {
|
|
return cliCommand{
|
|
Command: cmd,
|
|
actionWrappers: wrappers,
|
|
}
|
|
}
|
|
|
|
type cliCommand struct {
|
|
cli.Command
|
|
|
|
actionWrappers []actionWrapper
|
|
}
|
|
|
|
// WithCommand is disabled since it adds action wrappers and
|
|
// not tested in other cases.
|
|
func (c cliCommand) WithCommand(cli.Command) cli.Command {
|
|
panic("wrapped in cliCommand is expected to be leaf command")
|
|
}
|
|
|
|
func (c cliCommand) Action() cli.Action {
|
|
out := c.Command.Action()
|
|
if out == nil {
|
|
return nil
|
|
}
|
|
|
|
for _, wrapper := range c.actionWrappers {
|
|
out = wrapper(out)
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func actionWrapperParseConfig(next cli.Action) cli.Action {
|
|
return func(args []string, options map[string]string) int {
|
|
var result int
|
|
parseConfigOnce.Do(func() {
|
|
cfgpath, ok := options["config"]
|
|
if !ok || cfgpath == "" {
|
|
cfgpath = defaultConfigPath
|
|
}
|
|
|
|
payload, err := os.ReadFile(cfgpath)
|
|
if err != nil {
|
|
slog.Error("unable to read config file", slog.Any("err", err))
|
|
result = -1
|
|
return
|
|
}
|
|
|
|
err = json.Unmarshal(payload, ¤tConfig)
|
|
if err != nil {
|
|
slog.Error("unable to unmarshal config file", slog.Any("err", err))
|
|
result = -1
|
|
return
|
|
}
|
|
})
|
|
if result != 0 {
|
|
return result
|
|
}
|
|
|
|
return next(args, options)
|
|
}
|
|
}
|
|
|
|
func isJSONFormatEnabled(options map[string]string) bool {
|
|
_, ok := options[jsonOption.Key()]
|
|
return ok
|
|
}
|
|
|
|
type outputEncoderF func(any) error
|
|
|
|
func makeOutputEncoder(options map[string]string) outputEncoderF {
|
|
if isJSONFormatEnabled(options) {
|
|
out := json.NewEncoder(defaultOutput)
|
|
out.SetIndent("", " ")
|
|
return out.Encode
|
|
}
|
|
|
|
return outputEncoderF(func(a any) error {
|
|
_, err := fmt.Fprintf(defaultOutput, "%#v", a)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func makeLogger(options map[string]string) *slog.Logger {
|
|
level := slog.LevelInfo
|
|
if _, ok := options[debugOption.Key()]; ok {
|
|
level = slog.LevelDebug
|
|
}
|
|
|
|
opts := slog.HandlerOptions{
|
|
Level: level,
|
|
}
|
|
|
|
var h slog.Handler
|
|
if isJSONFormatEnabled(options) {
|
|
h = slog.NewJSONHandler(os.Stdout, &opts)
|
|
} else {
|
|
h = slog.NewTextHandler(os.Stdout, &opts)
|
|
}
|
|
|
|
return slog.New(h)
|
|
}
|
|
|
|
func makeSravniClient(ctx context.Context, log *slog.Logger, options map[string]string) (sravni.Client, error) {
|
|
_, isDebug := options[debugOption.Key()]
|
|
client, err := sravni.NewClient(ctx, log, isDebug)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("making new client: %w", err)
|
|
}
|
|
|
|
return client, nil
|
|
}
|
|
|
|
type baseAction struct {
|
|
ctx context.Context
|
|
log *slog.Logger
|
|
|
|
out outputEncoderF
|
|
}
|
|
|
|
func (ba *baseAction) getYDBConnection() (*adapters.YDBConnection, error) {
|
|
if currentConfig.YDB == (config.YDB{}) {
|
|
return nil, errors.SimpleError("no ydb config set")
|
|
}
|
|
|
|
ydbConn, err := adapters.NewYDBConnection(ba.ctx, currentConfig.YDB, ba.log.With(slog.String("db", "ydb")))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("making new ydb course repository: %w", err)
|
|
}
|
|
|
|
return ydbConn, nil
|
|
}
|
|
|
|
func (ba *baseAction) parse(_ []string, options map[string]string) (err error) {
|
|
ba.log = makeLogger(options).With(slog.String("component", "action"))
|
|
ba.out = makeOutputEncoder(options)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ba *baseAction) handle() error {
|
|
return errors.ErrNotImplemented
|
|
}
|
|
|
|
func (ba *baseAction) context() context.Context {
|
|
return ba.ctx
|
|
}
|
|
|
|
func newBaseAction(ctx context.Context) *baseAction {
|
|
return &baseAction{
|
|
ctx: ctx,
|
|
}
|
|
}
|