commands: add pre func for subcommand parameters

This commit is contained in:
Tulir Asokan 2025-04-28 13:38:18 +03:00
commit 06a292e1cc
4 changed files with 31 additions and 15 deletions

View file

@ -126,17 +126,20 @@ func (evt *Event[MetaType]) MarkRead() {
}
}
// PromoteFirstArgToCommand promotes the first argument to the command name.
//
// Command will be set to the lowercased first item in the Args list.
// Both Args and RawArgs will be updated to remove the first argument, but RawInput will be left as-is.
//
// The caller MUST check that there are args before calling this function.
func (evt *Event[MetaType]) PromoteFirstArgToCommand() {
// ShiftArg removes the first argument from the Args list and RawArgs data and returns it.
// RawInput will not be modified.
func (evt *Event[MetaType]) ShiftArg() string {
if len(evt.Args) == 0 {
panic(fmt.Errorf("PromoteFirstArgToCommand called with no args"))
return ""
}
evt.Command = strings.ToLower(evt.Args[0])
firstArg := evt.Args[0]
evt.RawArgs = strings.TrimLeft(strings.TrimPrefix(evt.RawArgs, evt.Args[0]), " ")
evt.Args = evt.Args[1:]
return firstArg
}
// UnshiftArg reverses ShiftArg by adding the given value to the beginning of the Args list and RawArgs data.
func (evt *Event[MetaType]) UnshiftArg(arg string) {
evt.RawArgs = arg + " " + evt.RawArgs
evt.Args = append([]string{arg}, evt.Args...)
}

View file

@ -11,6 +11,7 @@ import (
)
type Handler[MetaType any] struct {
// Func is the function that is called when the command is executed.
Func func(ce *Event[MetaType])
// Name is the primary name of the command. It must be lowercase.
@ -19,6 +20,10 @@ type Handler[MetaType any] struct {
Aliases []string
// Subcommands are subcommands of this command.
Subcommands []*Handler[MetaType]
// PreFunc is a function that is called before checking subcommands.
// It can be used to have parameters between subcommands (e.g. `!rooms <room ID> <command>`).
// Event.ShiftArg will likely be useful for implementing such parameters.
PreFunc func(ce *Event[MetaType])
subcommandContainer *CommandContainer[MetaType]
}

View file

@ -61,7 +61,7 @@ func (f AnyPreValidator[MetaType]) Validate(ce *Event[MetaType]) bool {
func ValidatePrefixCommand[MetaType any](prefix string) PreValidator[MetaType] {
return FuncPreValidator[MetaType](func(ce *Event[MetaType]) bool {
if ce.Command == prefix && len(ce.Args) > 0 {
ce.PromoteFirstArgToCommand()
ce.Command = strings.ToLower(ce.ShiftArg())
return true
}
return false

View file

@ -65,20 +65,31 @@ func (proc *Processor[MetaType]) Process(ctx context.Context, evt *event.Event)
if !proc.PreValidator.Validate(parsed) {
return
}
parsed.Proc = proc
parsed.Meta = proc.Meta
parsed.Ctx = ctx
handler := proc.GetHandler(parsed.Command)
if handler == nil {
return
}
parsed.Handler = handler
if handler.PreFunc != nil {
handler.PreFunc(parsed)
}
handlerChain := zerolog.Arr()
handlerChain.Str(handler.Name)
for handler.subcommandContainer != nil && len(parsed.Args) > 0 {
subHandler := handler.subcommandContainer.GetHandler(strings.ToLower(parsed.Args[0]))
if subHandler != nil {
handler = subHandler
parsed.ParentCommands = append(parsed.ParentCommands, parsed.Command)
handlerChain.Str(subHandler.Name)
parsed.PromoteFirstArgToCommand()
handler = subHandler
parsed.Command = strings.ToLower(parsed.ShiftArg())
parsed.Handler = subHandler
if subHandler.PreFunc != nil {
subHandler.PreFunc(parsed)
}
}
}
@ -95,9 +106,6 @@ func (proc *Processor[MetaType]) Process(ctx context.Context, evt *event.Event)
}
log = logWith.Logger()
parsed.Ctx = log.WithContext(ctx)
parsed.Handler = handler
parsed.Proc = proc
parsed.Meta = proc.Meta
log.Debug().Msg("Processing command")
handler.Func(parsed)