github.com/diamondburned/arikawa/v2@v2.1.0/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, command, subcommand and space trimmed. This
    74  // is used for commands that require more advanced parsing than the default
    75  // parser.
    76  type CustomParser interface {
    77  	CustomParse(arguments string) error
    78  }
    79  
    80  // RawArguments implements the CustomParser interface, which sets all the
    81  // arguments into it as raw as it could.
    82  type RawArguments string
    83  
    84  var _ CustomParser = (*RawArguments)(nil)
    85  
    86  func (a *RawArguments) CustomParse(arguments string) error {
    87  	*a = RawArguments(arguments)
    88  	return nil
    89  }
    90  
    91  // Argument is each argument in a method.
    92  type Argument struct {
    93  	String string
    94  	// Rule: pointer for structs, direct for primitives
    95  	rtype reflect.Type
    96  
    97  	// indicates if the type is referenced, meaning it's a pointer but not the
    98  	// original call.
    99  	pointer bool
   100  
   101  	// if nil, then manual
   102  	fn argumentValueFn
   103  
   104  	manual func(ManualParser, []string) error
   105  	custom func(CustomParser, string) error
   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  		if t.Kind() == reflect.Ptr {
   136  			t = t.Elem()
   137  		}
   138  
   139  		return &Argument{
   140  			String:  fromUsager(t),
   141  			rtype:   t,
   142  			pointer: ptr,
   143  			custom:  CustomParser.CustomParse,
   144  		}, nil
   145  	}
   146  
   147  	// This shouldn't be variadic either.
   148  	if !variadic && typeI.Implements(typeIManP) {
   149  		if t.Kind() == reflect.Ptr {
   150  			t = t.Elem()
   151  		}
   152  
   153  		return &Argument{
   154  			String:  fromUsager(t),
   155  			rtype:   t,
   156  			pointer: ptr,
   157  			manual:  ManualParser.ParseContent,
   158  		}, nil
   159  	}
   160  
   161  	if typeI.Implements(typeIParser) {
   162  		mt, ok := typeI.MethodByName("Parse")
   163  		if !ok {
   164  			panic("BUG: type IParser does not implement Parse")
   165  		}
   166  
   167  		avfn := func(input string) (reflect.Value, error) {
   168  			v := reflect.New(typeI.Elem())
   169  
   170  			ret := mt.Func.Call([]reflect.Value{
   171  				v, reflect.ValueOf(input),
   172  			})
   173  
   174  			_, err := errorReturns(ret)
   175  			if err != nil {
   176  				return nilV, err
   177  			}
   178  
   179  			if ptr {
   180  				v = v.Elem()
   181  			}
   182  
   183  			return v, nil
   184  		}
   185  
   186  		return &Argument{
   187  			String:  fromUsager(typeI),
   188  			rtype:   typeI,
   189  			pointer: ptr,
   190  			fn:      avfn,
   191  		}, nil
   192  	}
   193  
   194  	var fn argumentValueFn
   195  
   196  	switch t.Kind() {
   197  	case reflect.String:
   198  		fn = func(s string) (reflect.Value, error) {
   199  			return reflect.ValueOf(s), nil
   200  		}
   201  
   202  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   203  		fn = func(s string) (reflect.Value, error) {
   204  			i, err := strconv.ParseInt(s, 10, 64)
   205  			return quickRet(i, err, t)
   206  		}
   207  
   208  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   209  		fn = func(s string) (reflect.Value, error) {
   210  			u, err := strconv.ParseUint(s, 10, 64)
   211  			return quickRet(u, err, t)
   212  		}
   213  
   214  	case reflect.Float32, reflect.Float64:
   215  		fn = func(s string) (reflect.Value, error) {
   216  			f, err := strconv.ParseFloat(s, 64)
   217  			return quickRet(f, err, t)
   218  		}
   219  
   220  	case reflect.Bool:
   221  		fn = func(s string) (reflect.Value, error) {
   222  			switch s {
   223  			case "True", "TRUE", "true", "T", "t", "yes", "y", "Y", "1":
   224  				return reflect.ValueOf(true), nil
   225  			case "False", "FALSE", "false", "F", "f", "no", "n", "N", "0":
   226  				return reflect.ValueOf(false), nil
   227  			default:
   228  				return nilV, errors.New("invalid bool [true|false]")
   229  			}
   230  		}
   231  	}
   232  
   233  	if fn == nil {
   234  		return nil, errors.New("invalid type: " + t.String())
   235  	}
   236  
   237  	return &Argument{
   238  		String: fromUsager(t),
   239  		rtype:  t,
   240  		fn:     fn,
   241  	}, nil
   242  }
   243  
   244  func quickRet(v interface{}, err error, t reflect.Type) (reflect.Value, error) {
   245  	if err != nil {
   246  		return nilV, err
   247  	}
   248  
   249  	rv := reflect.ValueOf(v)
   250  
   251  	if t == nil {
   252  		return rv, nil
   253  	}
   254  
   255  	return rv.Convert(t), nil
   256  }
   257  
   258  func fromUsager(typeI reflect.Type) string {
   259  	if typeI.Implements(typeIUsager) {
   260  		mt, _ := typeI.MethodByName("Usage")
   261  
   262  		vs := mt.Func.Call([]reflect.Value{reflect.New(typeI).Elem()})
   263  		return vs[0].String()
   264  	}
   265  
   266  	s := strings.Split(typeI.String(), ".")
   267  	return s[len(s)-1]
   268  }