arikawa/bot/arguments.go

167 lines
3.5 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)
// Parseable implements a Parse(string) method for data structures that can be
// used as arguments.
type Parseable interface {
Parse(string) error
}
// ManaulParseable implements a ParseContent(string) method. If the library sees
// this for an argument, it will send all of the arguments (including the
// command) 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 ManualParseable interface {
// $0 will have its prefix trimmed.
ParseContent([]string) error
}
// RawArguments implements ManualParseable, in case you want to implement a
// custom argument parser. It borrows the library's argument parser.
2020-01-19 06:06:00 +00:00
type RawArguments struct {
2020-01-25 21:01:05 +00:00
Command string
2020-01-19 06:06:00 +00:00
Arguments []string
}
func (r *RawArguments) ParseContent(args []string) error {
2020-01-25 21:01:05 +00:00
r.Command = args[0]
if len(args) > 1 {
r.Arguments = args[1:]
}
2020-01-19 06:06:00 +00:00
return nil
}
func (r RawArguments) Arg(n int) string {
2020-01-25 21:01:05 +00:00
if n < 0 || n >= len(r.Arguments) {
return ""
}
return r.Arguments[n]
}
func (r RawArguments) After(n int) string {
2020-01-25 21:43:25 +00:00
if n < 0 || n >= len(r.Arguments) {
return ""
}
return strings.Join(r.Arguments[n:], " ")
}
func (r RawArguments) String() string {
2020-01-25 21:01:05 +00:00
return r.Command + " " + strings.Join(r.Arguments, " ")
}
func (r RawArguments) Length() int {
2020-01-25 21:43:25 +00:00
return len(r.Arguments)
}
// Argument is each argument in a method.
type Argument struct {
String string
// Rule: pointer for structs, direct for primitives
Type reflect.Type
// if nil, then manual
fn argumentValueFn
manual reflect.Method
}
2020-01-19 06:06:00 +00:00
// nilV, only used to return an error
var nilV = reflect.Value{}
func getArgumentValueFn(t reflect.Type) (argumentValueFn, error) {
if t.Implements(typeIParser) {
mt, ok := t.MethodByName("Parse")
if !ok {
panic("BUG: type IParser does not implement Parse")
}
return func(input string) (reflect.Value, error) {
v := reflect.New(t.Elem())
ret := mt.Func.Call([]reflect.Value{
v, reflect.ValueOf(input),
})
if err := errorReturns(ret); err != nil {
return nilV, err
}
return v, nil
}, nil
}
var fn argumentValueFn
switch t.Kind() {
case reflect.String:
fn = func(s string) (reflect.Value, error) {
return reflect.ValueOf(s), nil
}
case reflect.Int, reflect.Int8,
reflect.Int16, reflect.Int32, reflect.Int64:
fn = func(s string) (reflect.Value, error) {
i, err := strconv.ParseInt(s, 10, 64)
return quickRet(i, err, t)
}
case reflect.Uint, reflect.Uint8,
reflect.Uint16, reflect.Uint32, reflect.Uint64:
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 {
case "true", "yes", "y", "Y", "1":
return reflect.ValueOf(true), nil
case "false", "no", "n", "N", "0":
return reflect.ValueOf(false), nil
default:
return nilV, errors.New("invalid bool [true/false]")
}
}
}
if fn == nil {
return nil, errors.New("invalid type: " + t.String())
}
return fn, nil
}
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
}