writer.go 3.05 KB
Newer Older
Jan Winkelmann's avatar
Jan Winkelmann committed
1 2 3
package cmds

import (
4
	"encoding/json"
Jan Winkelmann's avatar
Jan Winkelmann committed
5 6 7
	"fmt"
	"io"
	"reflect"
8 9

	"github.com/ipfs/go-ipfs-cmds/cmdsutil"
Jan Winkelmann's avatar
Jan Winkelmann committed
10 11
)

12
var EmittedErr = fmt.Errorf("received an error")
Jan Winkelmann's avatar
Jan Winkelmann committed
13

Jan Winkelmann's avatar
Jan Winkelmann committed
14
func NewWriterResponseEmitter(w io.WriteCloser, res Response, enc func(io.Writer) Encoder) *WriterResponseEmitter {
15 16 17 18
	return &WriterResponseEmitter{
		w:   w,
		c:   w,
		enc: enc(w),
Jan Winkelmann's avatar
Jan Winkelmann committed
19
		req: res.Request(),
Jan Winkelmann's avatar
Jan Winkelmann committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
	}
}

func NewReaderResponse(r io.Reader, encType EncodingType, req Request) Response {
	return &readerResponse{
		req:     req,
		r:       r,
		encType: encType,
		dec:     Decoders[encType](r),
	}
}

type readerResponse struct {
	r       io.Reader
	encType EncodingType
	dec     Decoder

	req Request
	t   reflect.Type

	length uint64
41
	err    *cmdsutil.Error
Jan Winkelmann's avatar
Jan Winkelmann committed
42 43

	emitted bool
Jan Winkelmann's avatar
Jan Winkelmann committed
44 45
}

Jan Winkelmann's avatar
Jan Winkelmann committed
46 47
func (r *readerResponse) Request() Request {
	return r.req
Jan Winkelmann's avatar
Jan Winkelmann committed
48 49
}

50
func (r *readerResponse) Error() *cmdsutil.Error {
Jan Winkelmann's avatar
Jan Winkelmann committed
51 52 53 54 55 56 57 58
	return r.err
}

func (r *readerResponse) Length() uint64 {
	return r.length
}

func (r *readerResponse) Next() (interface{}, error) {
59 60 61
	a := &Any{}
	a.Add(cmdsutil.Error{})
	a.Add(r.req.Command().Type)
Jan Winkelmann's avatar
Jan Winkelmann committed
62

63 64 65 66 67 68 69 70 71 72 73
	err := r.dec.Decode(a)
	if err != nil {
		return nil, err
	}

	v := a.Interface()
	if err, ok := v.(error); ok {
		return err, EmittedErr
	}

	return v, nil
Jan Winkelmann's avatar
Jan Winkelmann committed
74 75
}

76
type WriterResponseEmitter struct {
Jan Winkelmann's avatar
Jan Winkelmann committed
77
	// TODO maybe make those public?
78 79 80
	w   io.Writer
	c   io.Closer
	enc Encoder
Jan Winkelmann's avatar
Jan Winkelmann committed
81
	req Request
Jan Winkelmann's avatar
Jan Winkelmann committed
82 83

	length *uint64
84
	err    *cmdsutil.Error
Jan Winkelmann's avatar
Jan Winkelmann committed
85 86

	emitted bool
Jan Winkelmann's avatar
Jan Winkelmann committed
87
	tees    []ResponseEmitter
Jan Winkelmann's avatar
Jan Winkelmann committed
88 89
}

90
func (re *WriterResponseEmitter) SetError(err interface{}, code cmdsutil.ErrorType) {
91
	*re.err = cmdsutil.Error{Message: fmt.Sprint(err), Code: code}
Jan Winkelmann's avatar
Jan Winkelmann committed
92 93 94 95

	for _, re_ := range re.tees {
		re_.SetError(err, code)
	}
Jan Winkelmann's avatar
Jan Winkelmann committed
96 97
}

98
func (re *WriterResponseEmitter) SetLength(length uint64) {
Jan Winkelmann's avatar
Jan Winkelmann committed
99 100 101 102 103
	if re.emitted {
		return
	}

	*re.length = length
Jan Winkelmann's avatar
Jan Winkelmann committed
104 105 106 107

	for _, re_ := range re.tees {
		re_.SetLength(length)
	}
Jan Winkelmann's avatar
Jan Winkelmann committed
108 109
}

110 111 112 113 114 115 116 117 118 119 120 121
func (re *WriterResponseEmitter) Close() error {
	return re.c.Close()
}

func (re *WriterResponseEmitter) Head() Head {
	return Head{
		Len: *re.length,
		Err: re.err,
	}
}

func (re *WriterResponseEmitter) Emit(v interface{}) error {
Jan Winkelmann's avatar
Jan Winkelmann committed
122 123
	re.emitted = true

Jan Winkelmann's avatar
Jan Winkelmann committed
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
	err := re.enc.Encode(v)
	if err != nil {
		return err
	}

	for _, re_ := range re.tees {
		err = re_.Emit(v)
		if err != nil {
			return err
		}
	}

	return nil
}

func (re *WriterResponseEmitter) Tee(re_ ResponseEmitter) {
	re.tees = append(re.tees, re_)

	// TODO first check whether length and error have been set
	re_.SetLength(*re.length)
	re_.SetError(re.err.Message, re.err.Code)
Jan Winkelmann's avatar
Jan Winkelmann committed
145
}
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

type Any struct {
	types []reflect.Type

	v interface{}
}

func (a *Any) UnmarshalJSON(data []byte) error {
	var (
		iv  interface{}
		err error
	)

	for _, t := range a.types {
		v := reflect.New(t)

		err = json.Unmarshal(data, v.Interface())
		if err == nil && v.Elem().Interface() != reflect.Zero(t).Interface() {
			a.v = v.Elem().Interface()
			return nil
		}
	}

	err = json.Unmarshal(data, &iv)
	a.v = iv

	return err
}

func (a *Any) Add(v interface{}) {
	t := reflect.TypeOf(v)
	if t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface {
		t = t.Elem()
	}

	a.types = append(a.types, t)
}

func (a *Any) Interface() interface{} {
	return a.v
}