1
0
Fork 0
mirror of https://github.com/diamondburned/arikawa.git synced 2024-11-16 11:54:29 +00:00
arikawa/utils/bot/arguments.go

269 lines
5.7 KiB
Go
Raw Normal View History

2020-01-19 06:06:00 +00:00
package bot
import (
"errors"
"reflect"
"strconv"
2020-01-25 21:01:05 +00:00
"strings"
2020-01-19 06:06:00 +00:00
)
type argumentValueFn func(string) (reflect.Value, error)
// Parser implements a Parse(string) method for data structures that can be
2020-01-19 06:06:00 +00:00
// used as arguments.
type Parser interface {
2020-01-19 06:06:00 +00:00
Parse(string) error
}
2020-04-09 04:25:50 +00:00
// Usager is used in place of the automatically parsed struct name for Parser
// and other interfaces.
type Usager interface {
Usage() string
}
// ManualParser has a ParseContent(string) method. If the library sees
2020-05-04 07:14:11 +00:00
// this for an argument, it will send all of the arguments into the method. If
// used, this should be the only argument followed after the Message Create
// event. Any more and the router will ignore.
type ManualParser interface {
2020-01-19 06:06:00 +00:00
// $0 will have its prefix trimmed.
ParseContent([]string) error
}
2020-05-14 03:42:31 +00:00
// ArgumentParts implements ManualParser, in case you want to parse arguments
2020-05-04 02:12:44 +00:00
// manually. It borrows the library's argument parser.
2020-05-14 03:42:31 +00:00
type ArgumentParts []string
2020-01-19 06:06:00 +00:00
2020-05-04 02:12:44 +00:00
var _ ManualParser = (*ArgumentParts)(nil)
2020-05-14 03:42:31 +00:00
// ParseContent implements ManualParser.
2020-05-04 02:12:44 +00:00
func (r *ArgumentParts) ParseContent(args []string) error {
2020-05-14 03:42:31 +00:00
*r = args
2020-01-19 06:06:00 +00:00
return nil
}
2020-05-04 02:12:44 +00:00
func (r ArgumentParts) Arg(n int) string {
2020-05-14 03:42:31 +00:00
if n < 0 || n >= len(r) {
2020-01-25 21:01:05 +00:00
return ""
}
2020-05-14 03:42:31 +00:00
return r[n]
2020-01-25 21:01:05 +00:00
}
2020-05-04 02:12:44 +00:00
func (r ArgumentParts) After(n int) string {
2020-05-14 03:42:31 +00:00
if n < 0 || n > len(r) {
2020-01-25 21:43:25 +00:00
return ""
}
2020-05-14 03:42:31 +00:00
return strings.Join(r[n:], " ")
2020-01-25 21:43:25 +00:00
}
2020-05-04 02:12:44 +00:00
func (r ArgumentParts) String() string {
2020-05-14 03:42:31 +00:00
return strings.Join(r, " ")
2020-01-25 21:01:05 +00:00
}
2020-05-04 02:12:44 +00:00
func (r ArgumentParts) Length() int {
2020-05-14 03:42:31 +00:00
return len(r)
}
// Usage implements Usager.
func (r ArgumentParts) Usage() string {
return "strings"
2020-01-25 21:43:25 +00:00
}
// CustomParser has a CustomParse method, which would be passed in the full
// message content with the prefix, command, subcommand and space trimmed. This
// is used for commands that require more advanced parsing than the default
// parser.
type CustomParser interface {
2020-05-04 02:12:44 +00:00
CustomParse(arguments string) error
}
// RawArguments implements the CustomParser interface, which sets all the
2020-05-04 02:12:44 +00:00
// arguments into it as raw as it could.
type RawArguments string
2020-05-04 02:12:44 +00:00
var _ CustomParser = (*RawArguments)(nil)
func (a *RawArguments) CustomParse(arguments string) error {
*a = RawArguments(arguments)
return nil
}
// Argument is each argument in a method.
type Argument struct {
String string
// Rule: pointer for structs, direct for primitives
2020-05-03 22:59:10 +00:00
rtype reflect.Type
// indicates if the type is referenced, meaning it's a pointer but not the
// original call.
pointer bool
// if nil, then manual
fn argumentValueFn
manual func(ManualParser, []string) error
custom func(CustomParser, string) error
}
2020-05-03 22:59:10 +00:00
func (a *Argument) Type() reflect.Type {
return a.rtype
}
var ShellwordsEscaper = strings.NewReplacer(
"\\", "\\\\",
)
2020-01-19 06:06:00 +00:00
// nilV, only used to return an error
var nilV = reflect.Value{}
func newArgument(t reflect.Type, variadic bool) (*Argument, error) {
// Allow array types if variadic is true.
2020-05-03 22:59:10 +00:00
if variadic && t.Kind() == reflect.Slice {
t = t.Elem()
}
var typeI = t
var ptr = false
if t.Kind() != reflect.Ptr {
typeI = reflect.PtrTo(t)
ptr = true
}
// This shouldn't be variadic.
if !variadic && typeI.Implements(typeICusP) {
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
return &Argument{
2020-05-14 03:42:31 +00:00
String: fromUsager(t),
rtype: t,
pointer: ptr,
custom: CustomParser.CustomParse,
}, nil
}
// This shouldn't be variadic either.
if !variadic && typeI.Implements(typeIManP) {
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
return &Argument{
2020-05-14 03:42:31 +00:00
String: fromUsager(t),
rtype: t,
pointer: ptr,
manual: ManualParser.ParseContent,
}, nil
}
if typeI.Implements(typeIParser) {
mt, ok := typeI.MethodByName("Parse")
2020-01-19 06:06:00 +00:00
if !ok {
panic("BUG: type IParser does not implement Parse")
}
avfn := func(input string) (reflect.Value, error) {
2020-02-05 04:29:45 +00:00
v := reflect.New(typeI.Elem())
2020-01-19 06:06:00 +00:00
ret := mt.Func.Call([]reflect.Value{
v, reflect.ValueOf(input),
})
2020-01-26 09:06:54 +00:00
_, err := errorReturns(ret)
if err != nil {
2020-01-19 06:06:00 +00:00
return nilV, err
}
if ptr {
v = v.Elem()
}
2020-01-19 06:06:00 +00:00
return v, nil
}
return &Argument{
2020-04-09 04:25:50 +00:00
String: fromUsager(typeI),
2020-05-03 22:59:10 +00:00
rtype: typeI,
pointer: ptr,
fn: avfn,
2020-01-19 06:06:00 +00:00
}, nil
}
var fn argumentValueFn
switch t.Kind() {
case reflect.String:
fn = func(s string) (reflect.Value, error) {
return reflect.ValueOf(s), nil
}
2020-05-03 22:59:10 +00:00
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2020-01-19 06:06:00 +00:00
fn = func(s string) (reflect.Value, error) {
i, err := strconv.ParseInt(s, 10, 64)
return quickRet(i, err, t)
}
2020-05-03 22:59:10 +00:00
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
2020-01-19 06:06:00 +00:00
fn = func(s string) (reflect.Value, error) {
u, err := strconv.ParseUint(s, 10, 64)
return quickRet(u, err, t)
}
case reflect.Float32, reflect.Float64:
fn = func(s string) (reflect.Value, error) {
f, err := strconv.ParseFloat(s, 64)
return quickRet(f, err, t)
}
case reflect.Bool:
fn = func(s string) (reflect.Value, error) {
switch s {
2020-02-05 04:29:45 +00:00
case "True", "TRUE", "true", "T", "t", "yes", "y", "Y", "1":
2020-01-19 06:06:00 +00:00
return reflect.ValueOf(true), nil
2020-02-05 04:29:45 +00:00
case "False", "FALSE", "false", "F", "f", "no", "n", "N", "0":
2020-01-19 06:06:00 +00:00
return reflect.ValueOf(false), nil
default:
2020-05-03 22:59:10 +00:00
return nilV, errors.New("invalid bool [true|false]")
2020-01-19 06:06:00 +00:00
}
}
}
if fn == nil {
return nil, errors.New("invalid type: " + t.String())
}
return &Argument{
2020-05-14 03:42:31 +00:00
String: fromUsager(t),
2020-05-03 22:59:10 +00:00
rtype: t,
fn: fn,
}, nil
2020-01-19 06:06:00 +00:00
}
func quickRet(v interface{}, err error, t reflect.Type) (reflect.Value, error) {
if err != nil {
return nilV, err
}
rv := reflect.ValueOf(v)
if t == nil {
return rv, nil
}
return rv.Convert(t), nil
}
2020-04-09 04:25:50 +00:00
func fromUsager(typeI reflect.Type) string {
if typeI.Implements(typeIUsager) {
2020-05-14 03:42:31 +00:00
mt, _ := typeI.MethodByName("Usage")
2020-05-03 22:59:10 +00:00
2020-05-14 03:42:31 +00:00
vs := mt.Func.Call([]reflect.Value{reflect.New(typeI).Elem()})
2020-04-09 04:25:50 +00:00
return vs[0].String()
}
2020-05-03 22:59:10 +00:00
2020-04-09 04:25:50 +00:00
s := strings.Split(typeI.String(), ".")
return s[len(s)-1]
}