response.go 2.7 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
  "io"
9 10
)

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

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

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

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

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

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

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

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
50 51
// Response is the result of a command request. Handlers write to the response,
// setting Error or Value. Response is returned to the client.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
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{}
72
  out   io.Writer
73 74 75 76 77 78 79 80 81 82 83 84 85 86
}

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
}

87 88 89 90
func (r *response) Stream() io.Writer {
  return r.out
}

91 92 93 94 95
func (r *response) Error() error {
	if r.err == nil {
		return nil
	}
	return r.err
Matt Bell's avatar
Matt Bell committed
96 97
}

98 99
func (r *response) SetError(err error, code ErrorType) {
	r.err = &Error{Message: err.Error(), Code: code}
100 101
}

102 103
func (r *response) Marshal() ([]byte, error) {
	if r.err == nil && r.value == nil {
Matt Bell's avatar
Matt Bell committed
104 105
		return nil, fmt.Errorf("No error or value set, there is nothing to marshal")
	}
106

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
107
	enc, ok := r.req.Option(EncShort)
108
	if !ok || enc.(string) == "" {
Matt Bell's avatar
Matt Bell committed
109 110 111
		return nil, fmt.Errorf("No encoding type was specified")
	}
	encType := EncodingType(strings.ToLower(enc.(string)))
Matt Bell's avatar
Matt Bell committed
112

Matt Bell's avatar
Matt Bell committed
113 114 115 116
	marshaller, ok := marshallers[encType]
	if !ok {
		return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc)
	}
117

118 119
	if r.err != nil {
		return marshaller(r.err)
Matt Bell's avatar
Matt Bell committed
120
	}
121 122 123 124
	return marshaller(r.value)
}

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