-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsmart.go
123 lines (107 loc) · 2.48 KB
/
smart.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package mpstream
import (
"bytes"
"encoding/json"
"fmt"
"io"
)
type PartBuilder func() (Part, error)
func StringPart(fieldname, value string) PartBuilder {
return func() (Part, error) {
return FormField(fieldname, []byte(value)), nil
}
}
func BytePart(fieldname string, value []byte) PartBuilder {
return func() (Part, error) {
return FormField(fieldname, value), nil
}
}
func JSONPart(fieldname string, value interface{}) PartBuilder {
return func() (Part, error) {
encoded, err := json.Marshal(value)
if err != nil {
return Part{}, fmt.Errorf(
`error marshalling field "%s" to json: %s`, fieldname, err,
)
}
return FormField(fieldname, encoded), nil
}
}
func FilePart(fieldname string, filename string) PartBuilder {
return func() (Part, error) {
p, _, err := FormFile(fieldname, filename)
if err != nil {
return Part{}, fmt.Errorf(
`error creating file part for field "%s": %s`, fieldname, err,
)
}
return p, nil
}
}
type SmartStream struct {
Stream
parts []Part
}
func NewSmart(builders ...PartBuilder) (*SmartStream, error) {
boundary, err := randomBoundary()
if err != nil {
return nil, err
}
return NewSmartWithBoundary(boundary, builders...)
}
func NewSmartWithBoundary(boundary string, builders ...PartBuilder) (*SmartStream, error) {
parts := make([]Part, len(builders))
for i, build := range builders {
var err error
parts[i], err = build()
if err != nil {
closeparts(parts) // swallows error
return nil, fmt.Errorf(
"mpstream: error building part[%d]: %s", i, err,
)
}
}
stream, err := NewWithBoundary(boundary, parts...)
if err != nil {
closeparts(parts) // swallows error
return nil, err
}
return &SmartStream{
Stream: *stream,
parts: parts,
}, nil
}
func (s *SmartStream) Close() error {
return closeparts(s.parts)
}
func closeparts(parts []Part) error {
var errs []error
for _, p := range parts {
if c, ok := p.Body.(io.Closer); ok {
if err := c.Close(); err != nil {
errs = append(errs, err)
}
}
}
if len(errs) == 0 {
return nil
} else {
return multiCloseErr{errs}
}
}
type multiCloseErr struct {
errs []error
}
func (e multiCloseErr) Error() string {
errs := e.errs
var msg bytes.Buffer
fmt.Fprintf(&msg, "mpstream: %d errors while closing parts: ", len(errs))
fmt.Fprintf(&msg, "[%d]: %s", 0, errs[0])
for i := 1; i < len(errs); i++ {
fmt.Fprintf(&msg, ", [%d]: %s", i, errs[i])
}
return msg.String()
}
func (e multiCloseErr) WrappedErrors() []error {
return e.errs
}