~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/gopkg.in/yaml.v2/encode.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}