github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/rc/params.go (about) 1 // Parameter parsing 2 3 package rc 4 5 import ( 6 "encoding/json" 7 "errors" 8 "fmt" 9 "math" 10 "net/http" 11 "strconv" 12 "time" 13 14 "github.com/rclone/rclone/fs" 15 ) 16 17 // Params is the input and output type for the Func 18 type Params map[string]interface{} 19 20 // ErrParamNotFound - this is returned from the Get* functions if the 21 // parameter isn't found along with a zero value of the requested 22 // item. 23 // 24 // Returning an error of this type from an rc.Func will cause the http 25 // method to return http.StatusBadRequest 26 type ErrParamNotFound string 27 28 // Error turns this error into a string 29 func (e ErrParamNotFound) Error() string { 30 return fmt.Sprintf("Didn't find key %q in input", string(e)) 31 } 32 33 // IsErrParamNotFound returns whether err is ErrParamNotFound 34 func IsErrParamNotFound(err error) bool { 35 _, isNotFound := err.(ErrParamNotFound) 36 return isNotFound 37 } 38 39 // NotErrParamNotFound returns true if err != nil and 40 // !IsErrParamNotFound(err) 41 // 42 // This is for checking error returns of the Get* functions to ignore 43 // error not found returns and take the default value. 44 func NotErrParamNotFound(err error) bool { 45 return err != nil && !IsErrParamNotFound(err) 46 } 47 48 // ErrParamInvalid - this is returned from the Get* functions if the 49 // parameter is invalid. 50 // 51 // Returning an error of this type from an rc.Func will cause the http 52 // method to return http.StatusBadRequest 53 type ErrParamInvalid struct { 54 error 55 } 56 57 // NewErrParamInvalid returns new ErrParamInvalid from given error 58 func NewErrParamInvalid(err error) ErrParamInvalid { 59 return ErrParamInvalid{err} 60 } 61 62 // IsErrParamInvalid returns whether err is ErrParamInvalid 63 func IsErrParamInvalid(err error) bool { 64 _, isInvalid := err.(ErrParamInvalid) 65 return isInvalid 66 } 67 68 // Reshape reshapes one blob of data into another via json serialization 69 // 70 // out should be a pointer type 71 // 72 // This isn't a very efficient way of dealing with this! 73 func Reshape(out interface{}, in interface{}) error { 74 b, err := json.Marshal(in) 75 if err != nil { 76 return fmt.Errorf("Reshape failed to Marshal: %w", err) 77 } 78 err = json.Unmarshal(b, out) 79 if err != nil { 80 return fmt.Errorf("Reshape failed to Unmarshal: %w", err) 81 } 82 return nil 83 } 84 85 // Copy shallow copies the Params 86 func (p Params) Copy() (out Params) { 87 out = make(Params, len(p)) 88 for k, v := range p { 89 out[k] = v 90 } 91 return out 92 } 93 94 // Get gets a parameter from the input 95 // 96 // If the parameter isn't found then error will be of type 97 // ErrParamNotFound and the returned value will be nil. 98 func (p Params) Get(key string) (interface{}, error) { 99 value, ok := p[key] 100 if !ok { 101 return nil, ErrParamNotFound(key) 102 } 103 return value, nil 104 } 105 106 // GetHTTPRequest gets a http.Request parameter associated with the request with the key "_request" 107 // 108 // If the parameter isn't found then error will be of type 109 // ErrParamNotFound and the returned value will be nil. 110 func (p Params) GetHTTPRequest() (*http.Request, error) { 111 key := "_request" 112 value, err := p.Get(key) 113 if err != nil { 114 return nil, err 115 } 116 request, ok := value.(*http.Request) 117 if !ok { 118 return nil, ErrParamInvalid{fmt.Errorf("expecting http.request value for key %q (was %T)", key, value)} 119 } 120 return request, nil 121 } 122 123 // GetHTTPResponseWriter gets a http.ResponseWriter parameter associated with the request with the key "_response" 124 // 125 // If the parameter isn't found then error will be of type 126 // ErrParamNotFound and the returned value will be nil. 127 func (p Params) GetHTTPResponseWriter() (http.ResponseWriter, error) { 128 key := "_response" 129 value, err := p.Get(key) 130 if err != nil { 131 return nil, err 132 } 133 request, ok := value.(http.ResponseWriter) 134 if !ok { 135 return nil, ErrParamInvalid{fmt.Errorf("expecting http.ResponseWriter value for key %q (was %T)", key, value)} 136 } 137 return request, nil 138 } 139 140 // GetString gets a string parameter from the input 141 // 142 // If the parameter isn't found then error will be of type 143 // ErrParamNotFound and the returned value will be "". 144 func (p Params) GetString(key string) (string, error) { 145 value, err := p.Get(key) 146 if err != nil { 147 return "", err 148 } 149 str, ok := value.(string) 150 if !ok { 151 return "", ErrParamInvalid{fmt.Errorf("expecting string value for key %q (was %T)", key, value)} 152 } 153 return str, nil 154 } 155 156 // GetInt64 gets an int64 parameter from the input 157 // 158 // If the parameter isn't found then error will be of type 159 // ErrParamNotFound and the returned value will be 0. 160 func (p Params) GetInt64(key string) (int64, error) { 161 value, err := p.Get(key) 162 if err != nil { 163 return 0, err 164 } 165 switch x := value.(type) { 166 case int: 167 return int64(x), nil 168 case int64: 169 return x, nil 170 case float64: 171 if x > math.MaxInt64 || x < math.MinInt64 { 172 return 0, ErrParamInvalid{fmt.Errorf("key %q (%v) overflows int64 ", key, value)} 173 } 174 return int64(x), nil 175 case string: 176 i, err := strconv.ParseInt(x, 10, 0) 177 if err != nil { 178 return 0, ErrParamInvalid{fmt.Errorf("couldn't parse key %q (%v) as int64: %w", key, value, err)} 179 } 180 return i, nil 181 } 182 return 0, ErrParamInvalid{fmt.Errorf("expecting int64 value for key %q (was %T)", key, value)} 183 } 184 185 // GetFloat64 gets a float64 parameter from the input 186 // 187 // If the parameter isn't found then error will be of type 188 // ErrParamNotFound and the returned value will be 0. 189 func (p Params) GetFloat64(key string) (float64, error) { 190 value, err := p.Get(key) 191 if err != nil { 192 return 0, err 193 } 194 switch x := value.(type) { 195 case float64: 196 return x, nil 197 case int: 198 return float64(x), nil 199 case int64: 200 return float64(x), nil 201 case string: 202 f, err := strconv.ParseFloat(x, 64) 203 if err != nil { 204 return 0, ErrParamInvalid{fmt.Errorf("couldn't parse key %q (%v) as float64: %w", key, value, err)} 205 } 206 return f, nil 207 } 208 return 0, ErrParamInvalid{fmt.Errorf("expecting float64 value for key %q (was %T)", key, value)} 209 } 210 211 // GetBool gets a boolean parameter from the input 212 // 213 // If the parameter isn't found then error will be of type 214 // ErrParamNotFound and the returned value will be false. 215 func (p Params) GetBool(key string) (bool, error) { 216 value, err := p.Get(key) 217 if err != nil { 218 return false, err 219 } 220 switch x := value.(type) { 221 case int: 222 return x != 0, nil 223 case int64: 224 return x != 0, nil 225 case float64: 226 return x != 0, nil 227 case bool: 228 return x, nil 229 case string: 230 b, err := strconv.ParseBool(x) 231 if err != nil { 232 return false, ErrParamInvalid{fmt.Errorf("couldn't parse key %q (%v) as bool: %w", key, value, err)} 233 } 234 return b, nil 235 } 236 return false, ErrParamInvalid{fmt.Errorf("expecting bool value for key %q (was %T)", key, value)} 237 } 238 239 // GetStruct gets a struct from key from the input into the struct 240 // pointed to by out. out must be a pointer type. 241 // 242 // If the parameter isn't found then error will be of type 243 // ErrParamNotFound and out will be unchanged. 244 func (p Params) GetStruct(key string, out interface{}) error { 245 value, err := p.Get(key) 246 if err != nil { 247 return err 248 } 249 err = Reshape(out, value) 250 if err != nil { 251 if valueStr, ok := value.(string); ok { 252 // try to unmarshal as JSON if string 253 err = json.Unmarshal([]byte(valueStr), out) 254 if err == nil { 255 return nil 256 } 257 } 258 return ErrParamInvalid{fmt.Errorf("key %q: %w", key, err)} 259 } 260 return nil 261 } 262 263 // GetStructMissingOK works like GetStruct but doesn't return an error 264 // if the key is missing 265 func (p Params) GetStructMissingOK(key string, out interface{}) error { 266 _, ok := p[key] 267 if !ok { 268 return nil 269 } 270 return p.GetStruct(key, out) 271 } 272 273 // GetDuration get the duration parameters from in 274 func (p Params) GetDuration(key string) (time.Duration, error) { 275 s, err := p.GetString(key) 276 if err != nil { 277 return 0, err 278 } 279 duration, err := fs.ParseDuration(s) 280 if err != nil { 281 return 0, ErrParamInvalid{fmt.Errorf("parse duration: %w", err)} 282 } 283 return duration, nil 284 } 285 286 // Error creates the standard response for an errored rc call using an 287 // rc.Param from a path, input Params, error and a suggested HTTP 288 // response code. 289 // 290 // It returns a Params and an updated status code 291 func Error(path string, in Params, err error, status int) (Params, int) { 292 // Adjust the status code for some well known errors 293 switch { 294 case errors.Is(err, fs.ErrorDirNotFound) || errors.Is(err, fs.ErrorObjectNotFound): 295 status = http.StatusNotFound 296 case IsErrParamInvalid(err) || IsErrParamNotFound(err): 297 status = http.StatusBadRequest 298 } 299 result := Params{ 300 "status": status, 301 "error": err.Error(), 302 "input": in, 303 "path": path, 304 } 305 return result, status 306 }