package console import ( "context" "fmt" "gitoa.ru/go-4devs/console/input" "gitoa.ru/go-4devs/console/output" ) type ( Action func(ctx context.Context, input input.Input, output output.Output) error Handle func(ctx context.Context, in input.Input, out output.Output, n Action) error Configure func(ctx context.Context, cfg *input.Definition) error Prepare func(ctx context.Context, cfg *input.Definition, n Configure) error Option func(*Command) ) // WithPrepare append middleware for configuration command. func WithPrepare(prepares ...Prepare) Option { return func(c *Command) { if c.Prepare != nil { prepares = append([]Prepare{c.Prepare}, prepares...) } c.Prepare = ChainPrepare(prepares...) } } // WithHandle append middleware for executed command. func WithHandle(handles ...Handle) Option { return func(c *Command) { if c.Handle != nil { handles = append([]Handle{c.Handle}, handles...) } c.Handle = ChainHandle(handles...) } } // WithHidden sets hidden command. func WithHidden(hidden bool) Option { return func(c *Command) { c.Hidden = hidden } } // WithName sets name command. func WithName(name string) Option { return func(c *Command) { c.Name = name } } type Command struct { // The name of the command. Name string // A short description of the usage of this command. Description string // A longer explanation of how the command works. Help string // Vervion command. Version string // Boolean to hide this command from help or completion. Hidden bool // Configures the current command. Configure Configure // The middleware for configures current command. Prepare Prepare // The function to call when this command is invoked. Execute Action // The middleware for executes current command. Handle Handle } func (c *Command) String() string { return fmt.Sprintf("name: %s, version: %s", c.Name, c.Version) } // With creates new command by parent and options. func (c *Command) With(opts ...Option) *Command { cmd := &Command{ Name: c.Name, Description: c.Description, Help: c.Help, Version: c.Version, Hidden: c.Hidden, Configure: c.Configure, Prepare: c.Prepare, Execute: c.Execute, Handle: c.Handle, } for _, opt := range opts { opt(cmd) } return cmd } // Run run command with input and output. func (c *Command) Run(ctx context.Context, in input.Input, out output.Output) error { if c.Execute == nil { return fmt.Errorf("%w", ErrExecuteNil) } if c.Handle != nil { return c.Handle(ctx, in, out, c.Execute) } return c.Execute(ctx, in, out) } // Init configures command. func (c *Command) Init(ctx context.Context, cfg *input.Definition) error { switch { case c.Prepare != nil && c.Configure != nil: return c.Prepare(ctx, cfg, c.Configure) case c.Prepare != nil: return c.Prepare(ctx, cfg, func(_ context.Context, _ *input.Definition) error { return nil }) case c.Configure != nil: return c.Configure(ctx, cfg) default: return nil } } // ChainPrepare creates middleware for configures command. func ChainPrepare(prepare ...Prepare) Prepare { num := len(prepare) if num == 1 { return prepare[0] } if num > 1 { lastI := num - 1 return func(ctx context.Context, def *input.Definition, next Configure) error { var ( chainHandler func(context.Context, *input.Definition) error curI int ) chainHandler = func(currentCtx context.Context, currentDef *input.Definition) error { if curI == lastI { return next(currentCtx, currentDef) } curI++ err := prepare[curI](currentCtx, currentDef, chainHandler) curI-- return err } return prepare[0](ctx, def, chainHandler) } } return func(ctx context.Context, cfg *input.Definition, next Configure) error { return next(ctx, cfg) } } // ChainHandle creates middleware for executes command. func ChainHandle(handlers ...Handle) Handle { num := len(handlers) if num == 1 { return handlers[0] } if num > 1 { lastI := num - 1 return func(ctx context.Context, in input.Input, out output.Output, next Action) error { var ( chainHandler func(context.Context, input.Input, output.Output) error curI int ) chainHandler = func(currentCtx context.Context, currentIn input.Input, currentOut output.Output) error { if curI == lastI { return next(currentCtx, currentIn, currentOut) } curI++ err := handlers[curI](currentCtx, currentIn, currentOut, chainHandler) curI-- return err } return handlers[0](ctx, in, out, chainHandler) } } return func(ctx context.Context, in input.Input, out output.Output, next Action) error { return next(ctx, in, out) } }