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 }