response.go 2.04 KB
Newer Older
Matt Bell's avatar
Matt Bell committed
1 2
package commands

3
import (
Matt Bell's avatar
Matt Bell committed
4 5 6 7
	"encoding/json"
	"encoding/xml"
	"fmt"
	"strings"
8 9
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10
// ErrorType signfies a category of errors
Matt Bell's avatar
Matt Bell committed
11
type ErrorType uint
Matt Bell's avatar
Matt Bell committed
12

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
13
// ErrorTypes convey what category of error ocurred
Matt Bell's avatar
Matt Bell committed
14
const (
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
15 16
	ErrNormal ErrorType = iota // general errors
	ErrClient                  // error was caused by the client, (e.g. invalid CLI usage)
Matt Bell's avatar
Matt Bell committed
17
	// TODO: add more types of errors for better error-specific handling
Matt Bell's avatar
Matt Bell committed
18 19
)

20 21
// Error is a struct for marshalling errors
type Error struct {
Matt Bell's avatar
Matt Bell committed
22 23
	Message string
	Code    ErrorType
24 25
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
26 27 28 29 30
func (e *Error) Error() string {
	return fmt.Sprintf("%d error: %s", e.Code, e.Message)
}

// EncodingType defines a supported encoding
31
type EncodingType string
Matt Bell's avatar
Matt Bell committed
32

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
33
// Supported EncodingType constants.
34
const (
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
35 36
	JSON = "json"
	XML  = "xml"
Matt Bell's avatar
Matt Bell committed
37
	// TODO: support more encoding types
38 39
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
40 41
// Marshaller is a function used by coding types.
// TODO this should just be a `coding.Codec`
Matt Bell's avatar
Matt Bell committed
42 43
type Marshaller func(v interface{}) ([]byte, error)

44
var marshallers = map[EncodingType]Marshaller{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
45 46
	JSON: json.Marshal,
	XML:  xml.Marshal,
47 48
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
49 50
// Response is the result of a command request. Handlers write to the response,
// setting Error or Value. Response is returned to the client.
Matt Bell's avatar
Matt Bell committed
51
type Response struct {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
52 53 54
	req   *Request
	Error *Error
	Value interface{}
Matt Bell's avatar
Matt Bell committed
55 56
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
57 58 59
// SetError updates the response Error.
func (r *Response) SetError(err error, code ErrorType) {
	r.Error = &Error{Message: err.Error(), Code: code}
60 61
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
62 63
// Marshal marshals out the response into a buffer. It uses the EncodingType
// on the Request to chose a Marshaller (Codec).
64
func (r *Response) Marshal() ([]byte, error) {
Matt Bell's avatar
Matt Bell committed
65 66 67
	if r.Error == nil && r.Value == nil {
		return nil, fmt.Errorf("No error or value set, there is nothing to marshal")
	}
68

69 70
	enc, ok := r.req.Option("enc")
	if !ok || enc.(string) == "" {
Matt Bell's avatar
Matt Bell committed
71 72 73
		return nil, fmt.Errorf("No encoding type was specified")
	}
	encType := EncodingType(strings.ToLower(enc.(string)))
Matt Bell's avatar
Matt Bell committed
74

Matt Bell's avatar
Matt Bell committed
75 76 77 78
	marshaller, ok := marshallers[encType]
	if !ok {
		return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc)
	}
79

Matt Bell's avatar
Matt Bell committed
80
	if r.Error != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
81
		return marshaller(r.Error)
Matt Bell's avatar
Matt Bell committed
82
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
83
	return marshaller(r.Value)
84
}