package main import ( "context" "fmt" "log/slog" "strconv" "git.loyso.art/frx/kurious/internal/domain" "git.loyso.art/frx/kurious/internal/infrastructure/interfaceadapters/courses/sravni" "github.com/teris-io/cli" ) const ( learningTypeOptName = "learning_type" courseThematicOptName = "course_thematic" ) func setupAPICommand(ctx context.Context) cli.Command { learningTypeOpt := cli.NewOption(learningTypeOptName, "Specify learning type"). WithChar('l'). WithType(cli.TypeString) courseThematic := cli.NewOption(courseThematicOptName, "Specify course thematic"). WithChar('t'). WithType(cli.TypeString) apiEducationListProducts := cli.NewCommand("list_products", "List products by some filters"). WithOption(learningTypeOpt). WithOption(courseThematic). WithOption(limitOption). WithOption(offsetOption). WithAction(newListProductAction(ctx)) apiEducation := cli.NewCommand("education", "Education related category"). WithCommand(apiEducationListProducts) return cli.NewCommand("api", "Interaction with API"). WithCommand(apiEducation) } type action interface { context() context.Context parse(args []string, options map[string]string) error handle() error } func asCLIAction(a action) cli.Action { return func(args []string, options map[string]string) int { ctx := a.context() log := makeLogger(options) err := a.parse(args, options) if err != nil { log.ErrorContext(ctx, "unable to parse args and opts", slog.Any("err", err)) return -1 } err = a.handle() if err != nil { log.ErrorContext(ctx, "unable to handle action", slog.Any("err", err)) return -1 } return 0 } } type baseAction struct { ctx context.Context client sravni.Client log *slog.Logger } func (ba *baseAction) parse(_ []string, options map[string]string) (err error) { ba.log = makeLogger(options).With(slog.String("component", "action")) ba.client, err = makeSravniClient(ba.ctx, ba.log, options) if err != nil { return err } return nil } func (ba *baseAction) handle() error { return domain.ErrNotImplemented } func (ba baseAction) context() context.Context { return ba.ctx } func newBaseAction(ctx context.Context) *baseAction { return &baseAction{ ctx: ctx, } } type listProductsActionParams struct { learningType string courseThematic string limit int offset int } type listProductsAction struct { *baseAction params listProductsActionParams } func newListProductAction(ctx context.Context) cli.Action { action := &listProductsAction{ baseAction: newBaseAction(ctx), } return asCLIAction(action) } func (a *listProductsAction) parse(args []string, options map[string]string) error { err := a.baseAction.parse(args, options) if err != nil { return err } var ok bool a.params.learningType, ok = options[learningTypeOptName] if !ok { return domain.SimpleError("learning_type is empty") } a.params.courseThematic, ok = options[courseThematicOptName] if !ok { return domain.SimpleError("course_thematic is empty") } if value, ok := options[limitOption.Key()]; ok { a.params.limit, _ = strconv.Atoi(value) } if value, ok := options[offsetOption.Key()]; ok { a.params.offset, _ = strconv.Atoi(value) } return nil } func (a *listProductsAction) handle() error { params := sravni.ListEducationProductsParams{ LearningType: a.params.learningType, CoursesThematics: a.params.courseThematic, Limit: a.params.limit, Offset: a.params.offset, } result, err := a.client.ListEducationalProducts(a.ctx, params) if err != nil { return fmt.Errorf("listing education products: %w", err) } a.log.InfoContext(a.ctx, "list education products result", slog.Any("result", result)) return nil }