/ dispatcher.go
dispatcher.go
  1  package dispatch2
  2  
  3  import (
  4  	"context"
  5  	"fmt"
  6  )
  7  
  8  type Dispatcher[Config any] interface {
  9  	After(middlewares ...MiddlewareFunc[Config])
 10  	Before(middlewares ...MiddlewareFunc[Config])
 11  	Commands() []Command[Config]
 12  	Config() *Config
 13  	Dispatch(context.Context, []string) error
 14  	Register(name, synopsis, usage string, aliases []string, executer Executer[Config]) error
 15  	RegisterCommand(Command[Config]) error
 16  	RegisterFunc(name, synopsis, usage string, aliases []string, executer ExecuterFunc[Config]) error
 17  }
 18  
 19  type dispatcher[Config any] struct {
 20  	afterMiddlewares  []MiddlewareFunc[Config]
 21  	beforeMiddlewares []MiddlewareFunc[Config]
 22  	commands          []Command[Config]
 23  	config            Config
 24  }
 25  
 26  var _ Dispatcher[struct{}] = &dispatcher[struct{}]{}
 27  
 28  func NewDispatcher[Config any]() *dispatcher[Config] {
 29  	return &dispatcher[Config]{}
 30  }
 31  
 32  func (d *dispatcher[Config]) After(middlewares ...MiddlewareFunc[Config]) {
 33  	for _, m := range middlewares {
 34  		d.afterMiddlewares = append(d.afterMiddlewares, invertMiddleware(m))
 35  	}
 36  }
 37  
 38  func (d *dispatcher[Config]) Before(middlewares ...MiddlewareFunc[Config]) {
 39  	d.beforeMiddlewares = append(d.beforeMiddlewares, middlewares...)
 40  }
 41  
 42  func (d *dispatcher[Config]) Config() *Config {
 43  	return &d.config
 44  }
 45  
 46  func (d *dispatcher[Config]) Commands() []Command[Config] {
 47  	return d.commands
 48  }
 49  
 50  func (d *dispatcher[Config]) Dispatch(ctx context.Context, args []string) error {
 51  	select {
 52  	case <-ctx.Done():
 53  		return ctx.Err()
 54  	default:
 55  		cmd, argsLeft, err := d.parseArgsForSubcommand(args)
 56  		if err != nil {
 57  			return err
 58  		}
 59  
 60  		if cmd == nil {
 61  			return fmt.Errorf("dispatch: command is nil")
 62  		}
 63  
 64  		if cmd.Executer() == nil {
 65  			return fmt.Errorf("dispatch: command has nil executer")
 66  		}
 67  
 68  		wrappedCmd := WrapExecuter(cmd)
 69  		if wrappedCmd == nil {
 70  			return fmt.Errorf("command: cannot execute nil executer")
 71  		}
 72  
 73  		// Apply AFTER middlewares (registration order, inverted)
 74  		for _, middleware := range d.afterMiddlewares {
 75  			wrappedCmd = middleware(wrappedCmd)
 76  		}
 77  
 78  		// Before middlewares: (reverse order)
 79  		for i := len(d.beforeMiddlewares) - 1; i >= 0; i-- {
 80  			wrappedCmd = d.beforeMiddlewares[i](wrappedCmd)
 81  		}
 82  
 83  		return wrappedCmd.Execute(ctx, d.Config(), argsLeft)
 84  	}
 85  }
 86  
 87  func (d *dispatcher[Config]) RegisterCommand(commands Command[Config]) error {
 88  	d.commands = append(d.commands, commands)
 89  	//TODO check if name or alias is taken
 90  	return nil
 91  }
 92  
 93  func (d *dispatcher[Config]) Register(name, synopsis, usage string, aliases []string, executer Executer[Config]) error {
 94  	return d.RegisterCommand(NewCommand(name, synopsis, usage, aliases, executer))
 95  }
 96  
 97  func (d *dispatcher[Config]) RegisterFunc(name, synopsis, usage string, aliases []string, executerFunc ExecuterFunc[Config]) error {
 98  	return d.RegisterCommand(NewCommand(name, synopsis, usage, aliases, executerFunc))
 99  }
100  
101  // unexported
102  
103  func (d *dispatcher[Config]) parseArgsForSubcommand(args []string) (Command[Config], []string, error) {
104  	return parseArgsForSubcommand(d.commands, args)
105  }