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 }