From 979164f4d245eecde5594104a555de3da66a9884 Mon Sep 17 00:00:00 2001 From: Gibheer Date: Thu, 22 Apr 2021 08:33:32 +0200 Subject: [PATCH] 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. --- server.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/server.go b/server.go index 1b6a416..e47d970 100644 --- a/server.go +++ b/server.go @@ -41,8 +41,11 @@ type ( // Request contains the method name and parameters requested by the client. Request struct { - Method string `json:"method"` - Params json.RawMessage `json:"params"` + // Method is the name to route to the correct function. + Method string `json:"method"` + // 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 +}