add functions to parse incoming request parameters

These helpers enable the parameter parsing into method specific structs.
As the parameter list is an array, the order of arguments is important.

Sadly type checks can be done at runtime, because all parameters are
converted to a list of interface{}. So if there is an error, it will
only result in an error at runtime, so be careful.
This commit is contained in:
Gibheer 2021-04-22 08:33:32 +02:00
parent 35bf70b410
commit 979164f4d2
1 changed files with 53 additions and 2 deletions

View File

@ -41,8 +41,11 @@ type (
// Request contains the method name and parameters requested by the client.
Request struct {
// Method is the name to route to the correct function.
Method string `json:"method"`
Params json.RawMessage `json:"params"`
// Params is the list of parameters in the request. These can be
// read directly or parsed using any of the parse methods.
Params []json.RawMessage `json:"params"`
}
// Response can have messages and/or a result to return the to client.
@ -170,3 +173,51 @@ func newIdent() (string, error) {
func (r *Response) AddMessage(level string, msg string, args ...interface{}) {
r.Messages[level] = append(r.Messages[level], fmt.Sprintf(msg, args...))
}
// Len returns the length of the parameter list.
func (r Request) Len() int {
return len(r.Params)
}
// Parse will parse the exact number of parameters into arguments.
// The boundaries are checked before.
func (r Request) Parse(args ...interface{}) error {
num := len(args)
if r.Len() != num {
return fmt.Errorf("expected %d parameters, got %d", num, r.Len())
}
return r.ParseAtLeast(num, args...)
}
// ParseAtLeast parses at least *num* many parameters into arguments.
// This function checks the boundaries before beginning to parse and returns
// errors if any boundary does not match.
func (r Request) ParseAtLeast(num int, args ...interface{}) error {
if len(args) < num {
return fmt.Errorf("requested %d arguments to parse, but only %d arguments given", num, len(args))
}
if r.Len() < num {
return fmt.Errorf("need %d parameters, but only got %d", num, r.Len())
}
if r.Len() > len(args) {
return fmt.Errorf("found %d parameters, when only %d are required", r.Len(), len(args))
}
for i, param := range r.Params {
if err := json.Unmarshal(param, args[i]); err != nil {
return fmt.Errorf("argument at position %d can't be parsed: %v", i, err)
}
}
return nil
}
// ParseAt unmarshalls the argument at *pos* into the container.
// If the position is outside the size of incoming arguments an error is raised.
func (r Request) ParseAt(pos int, container interface{}) error {
if pos >= r.Len() {
return fmt.Errorf("out of bounds") // TODO make generic ErrOutOfBounds
}
if err := json.Unmarshal(r.Params[pos], container); err != nil {
return fmt.Errorf("could not unmarshal parameter '%d': %s", pos, err)
}
return nil
}