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 +}