response.go 2.62 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.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
type Response interface {
	Request() Request

	// Set/Return the response Error
	SetError(err error, code ErrorType)
	Error() error

	// Sets/Returns the response value
	SetValue(interface{})
	Value() interface{}

	// Marshal marshals out the response into a buffer. It uses the EncodingType
	// on the Request to chose a Marshaller (Codec).
	Marshal() ([]byte, error)
}

type response struct {
	req   Request
	err   *Error
	value interface{}
}

func (r *response) Request() Request {
	return r.req
}

func (r *response) Value() interface{} {
	return r.value
}

func (r *response) SetValue(v interface{}) {
	r.value = v
}

func (r *response) Error() error {
	if r.err == nil {
		return nil
	}
	return r.err
Matt Bell's avatar
Matt Bell committed
90 91
}

92 93
func (r *response) SetError(err error, code ErrorType) {
	r.err = &Error{Message: err.Error(), Code: code}
94 95
}

96 97
func (r *response) Marshal() ([]byte, error) {
	if r.err == nil && r.value == nil {
Matt Bell's avatar
Matt Bell committed
98 99
		return nil, fmt.Errorf("No error or value set, there is nothing to marshal")
	}
100

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
101
	enc, ok := r.req.Option(EncShort)
102
	if !ok || enc.(string) == "" {
Matt Bell's avatar
Matt Bell committed
103 104 105
		return nil, fmt.Errorf("No encoding type was specified")
	}
	encType := EncodingType(strings.ToLower(enc.(string)))
Matt Bell's avatar
Matt Bell committed
106

Matt Bell's avatar
Matt Bell committed
107 108 109 110
	marshaller, ok := marshallers[encType]
	if !ok {
		return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc)
	}
111

112 113
	if r.err != nil {
		return marshaller(r.err)
Matt Bell's avatar
Matt Bell committed
114
	}
115 116 117 118 119 120
	return marshaller(r.value)
}

// NewResponse returns a response to match given Request
func NewResponse(req Request) Response {
	return &response{req: req}
121
}