179 lines
4.3 KiB
Go
179 lines
4.3 KiB
Go
package laniakea
|
|
|
|
import (
|
|
"errors"
|
|
"regexp"
|
|
|
|
"git.nix13.pw/scuroneko/extypes"
|
|
)
|
|
|
|
const (
|
|
CommandValueStringType CommandValueType = "string"
|
|
CommandValueIntType CommandValueType = "int"
|
|
CommandValueBoolType CommandValueType = "bool"
|
|
CommandValueAnyType CommandValueType = "any"
|
|
)
|
|
|
|
var (
|
|
CommandRegexInt = regexp.MustCompile("\\d+")
|
|
CommandRegexString = regexp.MustCompile(".+")
|
|
)
|
|
|
|
var (
|
|
ErrCmdArgCountMismatch = errors.New("command arg count mismatch")
|
|
ErrCmdArgRegexpMismatch = errors.New("command arg regexp mismatch")
|
|
)
|
|
|
|
type CommandValueType string
|
|
type CommandArg struct {
|
|
valueType CommandValueType
|
|
text string
|
|
regex *regexp.Regexp
|
|
required bool
|
|
}
|
|
|
|
func NewCommandArg(text string, valueType CommandValueType) *CommandArg {
|
|
regex := CommandRegexString
|
|
switch valueType {
|
|
case CommandValueIntType:
|
|
regex = CommandRegexInt
|
|
}
|
|
return &CommandArg{valueType, text, regex, false}
|
|
}
|
|
func (c *CommandArg) SetRequired() *CommandArg {
|
|
c.required = true
|
|
return c
|
|
}
|
|
|
|
type CommandExecutor[T DbContext] func(ctx *MsgContext, dbContext *T)
|
|
|
|
type Command[T DbContext] struct {
|
|
command string
|
|
description string
|
|
exec CommandExecutor[T]
|
|
args extypes.Slice[CommandArg]
|
|
middlewares extypes.Slice[Middleware[T]]
|
|
skipAutoCmd bool
|
|
}
|
|
|
|
func NewCommand[T any](exec CommandExecutor[T], command string, args ...CommandArg) *Command[T] {
|
|
return &Command[T]{command, "", exec, args, make(extypes.Slice[Middleware[T]], 0), false}
|
|
}
|
|
func (c *Command[T]) Use(m Middleware[T]) *Command[T] {
|
|
c.middlewares = c.middlewares.Push(m)
|
|
return c
|
|
}
|
|
func (c *Command[T]) SetDescription(desc string) *Command[T] {
|
|
c.description = desc
|
|
return c
|
|
}
|
|
func (c *Command[T]) SkipCommandAutoGen() *Command[T] {
|
|
c.skipAutoCmd = true
|
|
return c
|
|
}
|
|
func (c *Command[T]) validateArgs(args []string) error {
|
|
cmdArgs := c.args.Filter(func(e CommandArg) bool { return !e.required })
|
|
if len(args) < cmdArgs.Len() {
|
|
return ErrCmdArgCountMismatch
|
|
}
|
|
|
|
for i, arg := range args {
|
|
if i >= c.args.Len() {
|
|
break
|
|
}
|
|
cmdArg := c.args.Get(i)
|
|
if cmdArg.regex == nil {
|
|
continue
|
|
}
|
|
if !cmdArg.regex.MatchString(arg) {
|
|
return ErrCmdArgRegexpMismatch
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type Plugin[T DbContext] struct {
|
|
Name string
|
|
Commands map[string]Command[T]
|
|
Payloads map[string]Command[T]
|
|
Middlewares extypes.Slice[Middleware[T]]
|
|
}
|
|
|
|
func NewPlugin[T DbContext](name string) *Plugin[T] {
|
|
return &Plugin[T]{
|
|
name, map[string]Command[T]{},
|
|
map[string]Command[T]{}, extypes.Slice[Middleware[T]]{},
|
|
}
|
|
}
|
|
|
|
func (p *Plugin[T]) AddCommand(command *Command[T]) *Plugin[T] {
|
|
p.Commands[command.command] = *command
|
|
return p
|
|
}
|
|
func (p *Plugin[T]) NewCommand(exec CommandExecutor[T], command string, args ...CommandArg) *Command[T] {
|
|
return NewCommand(exec, command, args...)
|
|
}
|
|
func (p *Plugin[T]) AddPayload(command *Command[T]) *Plugin[T] {
|
|
p.Payloads[command.command] = *command
|
|
return p
|
|
}
|
|
func (p *Plugin[T]) AddMiddleware(middleware Middleware[T]) *Plugin[T] {
|
|
p.Middlewares = p.Middlewares.Push(middleware)
|
|
return p
|
|
}
|
|
|
|
func (p *Plugin[T]) executeCmd(cmd string, ctx *MsgContext, dbContext *T) {
|
|
command := p.Commands[cmd]
|
|
if err := command.validateArgs(ctx.Args); err != nil {
|
|
ctx.error(err)
|
|
return
|
|
}
|
|
command.exec(ctx, dbContext)
|
|
}
|
|
func (p *Plugin[T]) executePayload(payload string, ctx *MsgContext, dbContext *T) {
|
|
pl := p.Payloads[payload]
|
|
if err := pl.validateArgs(ctx.Args); err != nil {
|
|
ctx.error(err)
|
|
return
|
|
}
|
|
pl.exec(ctx, dbContext)
|
|
}
|
|
func (p *Plugin[T]) executeMiddlewares(ctx *MsgContext, db *T) bool {
|
|
for _, m := range p.Middlewares {
|
|
if !m.Execute(ctx, db) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
type MiddlewareExecutor[T DbContext] func(ctx *MsgContext, db *T) bool
|
|
|
|
// Middleware
|
|
// When async, returned value ignored
|
|
type Middleware[T DbContext] struct {
|
|
name string
|
|
executor MiddlewareExecutor[T]
|
|
order int
|
|
async bool
|
|
}
|
|
|
|
func NewMiddleware[T DbContext](name string, executor MiddlewareExecutor[T]) *Middleware[T] {
|
|
return &Middleware[T]{name, executor, 0, false}
|
|
}
|
|
func (m *Middleware[T]) SetOrder(order int) *Middleware[T] {
|
|
m.order = order
|
|
return m
|
|
}
|
|
func (m *Middleware[T]) SetAsync(async bool) *Middleware[T] {
|
|
m.async = async
|
|
return m
|
|
}
|
|
func (m *Middleware[T]) Execute(ctx *MsgContext, db *T) bool {
|
|
if m.async {
|
|
go m.executor(ctx, db)
|
|
return true
|
|
}
|
|
return m.executor(ctx, db)
|
|
}
|