github.com/diamondburned/arikawa@v1.3.14/bot/arguments.go (about)

     1  package bot
     2  
     3  import (
     4  	"errors"
     5  	"reflect"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  type argumentValueFn func(string) (reflect.Value, error)
    11  
    12  // Parser implements a Parse(string) method for data structures that can be
    13  // used as arguments.
    14  type Parser interface {
    15  	Parse(string) error
    16  }
    17  
    18  // Usager is used in place of the automatically parsed struct name for Parser
    19  // and other interfaces.
    20  type Usager interface {
    21  	Usage() string
    22  }
    23  
    24  // ManualParser has a ParseContent(string) method. If the library sees
    25  // this for an argument, it will send all of the arguments into the method. If
    26  // used, this should be the only argument followed after the Message Create
    27  // event. Any more and the router will ignore.
    28  type ManualParser interface {
    29  	// $0 will have its prefix trimmed.
    30  	ParseContent([]string) error
    31  }
    32  
    33  // ArgumentParts implements ManualParser, in case you want to parse arguments
    34  // manually. It borrows the library's argument parser.
    35  type ArgumentParts []string
    36  
    37  var _ ManualParser = (*ArgumentParts)(nil)
    38  
    39  // ParseContent implements ManualParser.
    40  func (r *ArgumentParts) ParseContent(args []string) error {
    41  	*r = args
    42  	return nil
    43  }
    44  
    45  func (r ArgumentParts) Arg(n int) string {
    46  	if n < 0 || n >= len(r) {
    47  		return ""
    48  	}
    49  	return r[n]
    50  }
    51  
    52  func (r ArgumentParts) After(n int) string {
    53  	if n < 0 || n > len(r) {
    54  		return ""
    55  	}
    56  	return strings.Join(r[n:], " ")
    57  }
    58  
    59  func (r ArgumentParts) String() string {
    60  	return strings.Join(r, " ")
    61  }
    62  
    63  func (r ArgumentParts) Length() int {
    64  	return len(r)
    65  }
    66  
    67  // Usage implements Usager.
    68  func (r ArgumentParts) Usage() string {
    69  	return "strings"
    70  }
    71  
    72  // CustomParser has a CustomParse method, which would be passed in the full
    73  // message content with the prefix and command trimmed. This is used
    74  // for commands that require more advanced parsing than the default parser.
    75  //
    76  // Keep in mind that this does not trim arguments before it.
    77  type CustomParser interface {
    78  	CustomParse(arguments string) error
    79  }
    80  
    81  // RawArguments implements the CustomParser interface, which sets all the
    82  // arguments into it as raw as it could.
    83  type RawArguments string
    84  
    85  var _ CustomParser = (*RawArguments)(nil)
    86  
    87  func (a *RawArguments) CustomParse(arguments string) error {
    88  	*a = RawArguments(arguments)
    89  	return nil
    90  }
    91  
    92  // Argument is each argument in a method.
    93  type Argument struct {
    94  	String string
    95  	// Rule: pointer for structs, direct for primitives
    96  	rtype reflect.Type
    97  
    98  	// indicates if the type is referenced, meaning it's a pointer but not the
    99  	// original call.
   100  	pointer bool
   101  
   102  	// if nil, then manual
   103  	fn     argumentValueFn
   104  	manual *reflect.Method
   105  	custom *reflect.Method
   106  }
   107  
   108  func (a *Argument) Type() reflect.Type {
   109  	return a.rtype
   110  }
   111  
   112  var ShellwordsEscaper = strings.NewReplacer(
   113  	"\\", "\\\\",
   114  )
   115  
   116  // nilV, only used to return an error
   117  var nilV = reflect.Value{}
   118  
   119  func newArgument(t reflect.Type, variadic bool) (*Argument, error) {
   120  	// Allow array types if variadic is true.
   121  	if variadic && t.Kind() == reflect.Slice {
   122  		t = t.Elem()
   123  	}
   124  
   125  	var typeI = t
   126  	var ptr = false
   127  
   128  	if t.Kind() != reflect.Ptr {
   129  		typeI = reflect.PtrTo(t)
   130  		ptr = true
   131  	}
   132  
   133  	// This shouldn't be variadic.
   134  	if !variadic && typeI.Implements(typeICusP) {
   135  		mt, _ := typeI.MethodByName("CustomParse")
   136  
   137  		// TODO: maybe ish?
   138  		if t.Kind() == reflect.Ptr {
   139  			t = t.Elem()
   140  		}
   141  
   142  		return &Argument{
   143  			String:  fromUsager(t),
   144  			rtype:   t,
   145  			pointer: ptr,
   146  			custom:  &mt,
   147  		}, nil
   148  	}
   149  
   150  	// This shouldn't be variadic either.
   151  	if !variadic && typeI.Implements(typeIManP) {
   152  		mt, _ := typeI.MethodByName("ParseContent")
   153  
   154  		if t.Kind() == reflect.Ptr {
   155  			t = t.Elem()
   156  		}
   157  
   158  		return &Argument{
   159  			String:  fromUsager(t),
   160  			rtype:   t,
   161  			pointer: ptr,
   162  			manual:  &mt,
   163  		}, nil
   164  	}
   165  
   166  	if typeI.Implements(typeIParser) {
   167  		mt, ok := typeI.MethodByName("Parse")
   168  		if !ok {
   169  			panic("BUG: type IParser does not implement Parse")
   170  		}
   171  
   172  		avfn := func(input string) (reflect.Value, error) {
   173  			v := reflect.New(typeI.Elem())
   174  
   175  			ret := mt.Func.Call([]reflect.Value{
   176  				v, reflect.ValueOf(input),
   177  			})
   178  
   179  			_, err := errorReturns(ret)
   180  			if err != nil {
   181  				return nilV, err
   182  			}
   183  
   184  			if ptr {
   185  				v = v.Elem()
   186  			}
   187  
   188  			return v, nil
   189  		}
   190  
   191  		return &Argument{
   192  			String:  fromUsager(typeI),
   193  			rtype:   typeI,
   194  			pointer: ptr,
   195  			fn:      avfn,
   196  		}, nil
   197  	}
   198  
   199  	var fn argumentValueFn
   200  
   201  	switch t.Kind() {
   202  	case reflect.String:
   203  		fn = func(s string) (reflect.Value, error) {
   204  			return reflect.ValueOf(s), nil
   205  		}
   206  
   207  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   208  		fn = func(s string) (reflect.Value, error) {
   209  			i, err := strconv.ParseInt(s, 10, 64)
   210  			return quickRet(i, err, t)
   211  		}
   212  
   213  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   214  		fn = func(s string) (reflect.Value, error) {
   215  			u, err := strconv.ParseUint(s, 10, 64)
   216  			return quickRet(u, err, t)
   217  		}
   218  
   219  	case reflect.Float32, reflect.Float64:
   220  		fn = func(s string) (reflect.Value, error) {
   221  			f, err := strconv.ParseFloat(s, 64)
   222  			return quickRet(f, err, t)
   223  		}
   224  
   225  	case reflect.Bool:
   226  		fn = func(s string) (reflect.Value, error) {
   227  			switch s {
   228  			case "True", "TRUE", "true", "T", "t", "yes", "y", "Y", "1":
   229  				return reflect.ValueOf(true), nil
   230  			case "False", "FALSE", "false", "F", "f", "no", "n", "N", "0":
   231  				return reflect.ValueOf(false), nil
   232  			default:
   233  				return nilV, errors.New("invalid bool [true|false]")
   234  			}
   235  		}
   236  	}
   237  
   238  	if fn == nil {
   239  		return nil, errors.New("invalid type: " + t.String())
   240  	}
   241  
   242  	return &Argument{
   243  		String: fromUsager(t),
   244  		rtype:  t,
   245  		fn:     fn,
   246  	}, nil
   247  }
   248  
   249  func quickRet(v interface{}, err error, t reflect.Type) (reflect.Value, error) {
   250  	if err != nil {
   251  		return nilV, err
   252  	}
   253  
   254  	rv := reflect.ValueOf(v)
   255  
   256  	if t == nil {
   257  		return rv, nil
   258  	}
   259  
   260  	return rv.Convert(t), nil
   261  }
   262  
   263  func fromUsager(typeI reflect.Type) string {
   264  	if typeI.Implements(typeIUsager) {
   265  		mt, _ := typeI.MethodByName("Usage")
   266  
   267  		vs := mt.Func.Call([]reflect.Value{reflect.New(typeI).Elem()})
   268  		return vs[0].String()
   269  	}
   270  
   271  	s := strings.Split(typeI.String(), ".")
   272  	return s[len(s)-1]
   273  }