// Package cps provides the command processing system. package cps import ( "context" "errors" "fmt" "strings" ) type Command struct { Context context.Context Raw string // The original command passed to the agent. Command string // Which command was invoked? Arg string // Input string sans the leading command. Args []string // Tokenised list of arguments. Meta map[string]string // Sometimes you need additional information. } func (cmd *Command) Handle() (*Response, error) { return handle(cmd) } // Parse takes a command string from an input string and parent context. func Parse(ctx context.Context, line string) (*Command, error) { line = strings.TrimSpace(line) args := strings.Fields(line) if len(args) == 0 { return nil, errors.New("cps: no command line provided") } cmd := &Command{ Context: ctx, Raw: line, Command: strings.ToLower(args[0]), Args: args[1:], Meta: map[string]string{}, } cmd.Arg = strings.TrimSpace(strings.TrimPrefix(cmd.Raw, cmd.Command)) return cmd, nil } // ParseBackground calls Parse(context.Background, line). func ParseBackground(line string) (*Command, error) { return Parse(context.Background(), line) } type Response struct { Message string Error error } func (r *Response) String() string { if r == nil { return "carrier signal lost" } out := "" if r.Message != "" { out += r.Message } else if r.Error != nil { out = fmt.Sprintf("Error: %s", r.Error) } return out } var registry = map[string]func(*Command) (*Response, error){} func Register(command string, handler func(*Command) (*Response, error)) { if _, ok := registry[command]; ok { panic("handler for " + command + " already registered!") } registry[command] = handler } func handle(cmd *Command) (*Response, error) { handler, ok := registry[cmd.Command] if !ok { err := fmt.Errorf("cps: no handler for command '%s'", cmd.Command) return &Response{ Message: fmt.Sprintf("%s is not a recognized command", cmd.Command), Error: err, }, err } return handler(cmd) } func Handle(ctx context.Context, line string) (*Response, error) { cmd, err := Parse(ctx, line) if err != nil { return &Response{ Message: "Unable to process message", Error: err, }, err } return cmd.Handle() }