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  }