OSDN Git Service

new repo
[bytom/vapor.git] / vendor / gopkg.in / yaml.v2 / encode.go
1 package yaml
2
3 import (
4         "encoding"
5         "fmt"
6         "reflect"
7         "regexp"
8         "sort"
9         "strconv"
10         "strings"
11         "time"
12 )
13
14 type encoder struct {
15         emitter yaml_emitter_t
16         event   yaml_event_t
17         out     []byte
18         flow    bool
19 }
20
21 func newEncoder() (e *encoder) {
22         e = &encoder{}
23         e.must(yaml_emitter_initialize(&e.emitter))
24         yaml_emitter_set_output_string(&e.emitter, &e.out)
25         yaml_emitter_set_unicode(&e.emitter, true)
26         e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING))
27         e.emit()
28         e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true))
29         e.emit()
30         return e
31 }
32
33 func (e *encoder) finish() {
34         e.must(yaml_document_end_event_initialize(&e.event, true))
35         e.emit()
36         e.emitter.open_ended = false
37         e.must(yaml_stream_end_event_initialize(&e.event))
38         e.emit()
39 }
40
41 func (e *encoder) destroy() {
42         yaml_emitter_delete(&e.emitter)
43 }
44
45 func (e *encoder) emit() {
46         // This will internally delete the e.event value.
47         if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT {
48                 e.must(false)
49         }
50 }
51
52 func (e *encoder) must(ok bool) {
53         if !ok {
54                 msg := e.emitter.problem
55                 if msg == "" {
56                         msg = "unknown problem generating YAML content"
57                 }
58                 failf("%s", msg)
59         }
60 }
61
62 func (e *encoder) marshal(tag string, in reflect.Value) {
63         if !in.IsValid() {
64                 e.nilv()
65                 return
66         }
67         iface := in.Interface()
68         if m, ok := iface.(Marshaler); ok {
69                 v, err := m.MarshalYAML()
70                 if err != nil {
71                         fail(err)
72                 }
73                 if v == nil {
74                         e.nilv()
75                         return
76                 }
77                 in = reflect.ValueOf(v)
78         } else if m, ok := iface.(encoding.TextMarshaler); ok {
79                 text, err := m.MarshalText()
80                 if err != nil {
81                         fail(err)
82                 }
83                 in = reflect.ValueOf(string(text))
84         }
85         switch in.Kind() {
86         case reflect.Interface:
87                 if in.IsNil() {
88                         e.nilv()
89                 } else {
90                         e.marshal(tag, in.Elem())
91                 }
92         case reflect.Map:
93                 e.mapv(tag, in)
94         case reflect.Ptr:
95                 if in.IsNil() {
96                         e.nilv()
97                 } else {
98                         e.marshal(tag, in.Elem())
99                 }
100         case reflect.Struct:
101                 e.structv(tag, in)
102         case reflect.Slice:
103                 if in.Type().Elem() == mapItemType {
104                         e.itemsv(tag, in)
105                 } else {
106                         e.slicev(tag, in)
107                 }
108         case reflect.String:
109                 e.stringv(tag, in)
110         case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
111                 if in.Type() == durationType {
112                         e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String()))
113                 } else {
114                         e.intv(tag, in)
115                 }
116         case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
117                 e.uintv(tag, in)
118         case reflect.Float32, reflect.Float64:
119                 e.floatv(tag, in)
120         case reflect.Bool:
121                 e.boolv(tag, in)
122         default:
123                 panic("cannot marshal type: " + in.Type().String())
124         }
125 }
126
127 func (e *encoder) mapv(tag string, in reflect.Value) {
128         e.mappingv(tag, func() {
129                 keys := keyList(in.MapKeys())
130                 sort.Sort(keys)
131                 for _, k := range keys {
132                         e.marshal("", k)
133                         e.marshal("", in.MapIndex(k))
134                 }
135         })
136 }
137
138 func (e *encoder) itemsv(tag string, in reflect.Value) {
139         e.mappingv(tag, func() {
140                 slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem)
141                 for _, item := range slice {
142                         e.marshal("", reflect.ValueOf(item.Key))
143                         e.marshal("", reflect.ValueOf(item.Value))
144                 }
145         })
146 }
147
148 func (e *encoder) structv(tag string, in reflect.Value) {
149         sinfo, err := getStructInfo(in.Type())
150         if err != nil {
151                 panic(err)
152         }
153         e.mappingv(tag, func() {
154                 for _, info := range sinfo.FieldsList {
155                         var value reflect.Value
156                         if info.Inline == nil {
157                                 value = in.Field(info.Num)
158                         } else {
159                                 value = in.FieldByIndex(info.Inline)
160                         }
161                         if info.OmitEmpty && isZero(value) {
162                                 continue
163                         }
164                         e.marshal("", reflect.ValueOf(info.Key))
165                         e.flow = info.Flow
166                         e.marshal("", value)
167                 }
168                 if sinfo.InlineMap >= 0 {
169                         m := in.Field(sinfo.InlineMap)
170                         if m.Len() > 0 {
171                                 e.flow = false
172                                 keys := keyList(m.MapKeys())
173                                 sort.Sort(keys)
174                                 for _, k := range keys {
175                                         if _, found := sinfo.FieldsMap[k.String()]; found {
176                                                 panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
177                                         }
178                                         e.marshal("", k)
179                                         e.flow = false
180                                         e.marshal("", m.MapIndex(k))
181                                 }
182                         }
183                 }
184         })
185 }
186
187 func (e *encoder) mappingv(tag string, f func()) {
188         implicit := tag == ""
189         style := yaml_BLOCK_MAPPING_STYLE
190         if e.flow {
191                 e.flow = false
192                 style = yaml_FLOW_MAPPING_STYLE
193         }
194         e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
195         e.emit()
196         f()
197         e.must(yaml_mapping_end_event_initialize(&e.event))
198         e.emit()
199 }
200
201 func (e *encoder) slicev(tag string, in reflect.Value) {
202         implicit := tag == ""
203         style := yaml_BLOCK_SEQUENCE_STYLE
204         if e.flow {
205                 e.flow = false
206                 style = yaml_FLOW_SEQUENCE_STYLE
207         }
208         e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
209         e.emit()
210         n := in.Len()
211         for i := 0; i < n; i++ {
212                 e.marshal("", in.Index(i))
213         }
214         e.must(yaml_sequence_end_event_initialize(&e.event))
215         e.emit()
216 }
217
218 // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
219 //
220 // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
221 // in YAML 1.2 and by this package, but these should be marshalled quoted for
222 // the time being for compatibility with other parsers.
223 func isBase60Float(s string) (result bool) {
224         // Fast path.
225         if s == "" {
226                 return false
227         }
228         c := s[0]
229         if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
230                 return false
231         }
232         // Do the full match.
233         return base60float.MatchString(s)
234 }
235
236 // From http://yaml.org/type/float.html, except the regular expression there
237 // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
238 var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
239
240 func (e *encoder) stringv(tag string, in reflect.Value) {
241         var style yaml_scalar_style_t
242         s := in.String()
243         rtag, rs := resolve("", s)
244         if rtag == yaml_BINARY_TAG {
245                 if tag == "" || tag == yaml_STR_TAG {
246                         tag = rtag
247                         s = rs.(string)
248                 } else if tag == yaml_BINARY_TAG {
249                         failf("explicitly tagged !!binary data must be base64-encoded")
250                 } else {
251                         failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
252                 }
253         }
254         if tag == "" && (rtag != yaml_STR_TAG || isBase60Float(s)) {
255                 style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
256         } else if strings.Contains(s, "\n") {
257                 style = yaml_LITERAL_SCALAR_STYLE
258         } else {
259                 style = yaml_PLAIN_SCALAR_STYLE
260         }
261         e.emitScalar(s, "", tag, style)
262 }
263
264 func (e *encoder) boolv(tag string, in reflect.Value) {
265         var s string
266         if in.Bool() {
267                 s = "true"
268         } else {
269                 s = "false"
270         }
271         e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
272 }
273
274 func (e *encoder) intv(tag string, in reflect.Value) {
275         s := strconv.FormatInt(in.Int(), 10)
276         e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
277 }
278
279 func (e *encoder) uintv(tag string, in reflect.Value) {
280         s := strconv.FormatUint(in.Uint(), 10)
281         e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
282 }
283
284 func (e *encoder) floatv(tag string, in reflect.Value) {
285         // FIXME: Handle 64 bits here.
286         s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32)
287         switch s {
288         case "+Inf":
289                 s = ".inf"
290         case "-Inf":
291                 s = "-.inf"
292         case "NaN":
293                 s = ".nan"
294         }
295         e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
296 }
297
298 func (e *encoder) nilv() {
299         e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
300 }
301
302 func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
303         implicit := tag == ""
304         e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
305         e.emit()
306 }