~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/gopkg.in/yaml.v1/yaml.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 implements YAML support for the Go language.
 
2
//
 
3
// Source code and other details for the project are available at GitHub:
 
4
//
 
5
//   https://github.com/go-yaml/yaml
 
6
//
 
7
package yaml
 
8
 
 
9
import (
 
10
        "errors"
 
11
        "fmt"
 
12
        "reflect"
 
13
        "strings"
 
14
        "sync"
 
15
)
 
16
 
 
17
type yamlError string
 
18
 
 
19
func fail(msg string) {
 
20
        panic(yamlError(msg))
 
21
}
 
22
 
 
23
func handleErr(err *error) {
 
24
        if r := recover(); r != nil {
 
25
                if e, ok := r.(yamlError); ok {
 
26
                        *err = errors.New("YAML error: " + string(e))
 
27
                } else {
 
28
                        panic(r)
 
29
                }
 
30
        }
 
31
}
 
32
 
 
33
// The Setter interface may be implemented by types to do their own custom
 
34
// unmarshalling of YAML values, rather than being implicitly assigned by
 
35
// the yaml package machinery. If setting the value works, the method should
 
36
// return true.  If it returns false, the value is considered unsupported
 
37
// and is omitted from maps and slices.
 
38
type Setter interface {
 
39
        SetYAML(tag string, value interface{}) bool
 
40
}
 
41
 
 
42
// The Getter interface is implemented by types to do their own custom
 
43
// marshalling into a YAML tag and value.
 
44
type Getter interface {
 
45
        GetYAML() (tag string, value interface{})
 
46
}
 
47
 
 
48
// Unmarshal decodes the first document found within the in byte slice
 
49
// and assigns decoded values into the out value.
 
50
//
 
51
// Maps and pointers (to a struct, string, int, etc) are accepted as out
 
52
// values.  If an internal pointer within a struct is not initialized,
 
53
// the yaml package will initialize it if necessary for unmarshalling
 
54
// the provided data. The out parameter must not be nil.
 
55
//
 
56
// The type of the decoded values and the type of out will be considered,
 
57
// and Unmarshal will do the best possible job to unmarshal values
 
58
// appropriately.  It is NOT considered an error, though, to skip values
 
59
// because they are not available in the decoded YAML, or if they are not
 
60
// compatible with the out value. To ensure something was properly
 
61
// unmarshaled use a map or compare against the previous value for the
 
62
// field (usually the zero value).
 
63
//
 
64
// Struct fields are only unmarshalled if they are exported (have an
 
65
// upper case first letter), and are unmarshalled using the field name
 
66
// lowercased as the default key. Custom keys may be defined via the
 
67
// "yaml" name in the field tag: the content preceding the first comma
 
68
// is used as the key, and the following comma-separated options are
 
69
// used to tweak the marshalling process (see Marshal).
 
70
// Conflicting names result in a runtime error.
 
71
//
 
72
// For example:
 
73
//
 
74
//     type T struct {
 
75
//         F int `yaml:"a,omitempty"`
 
76
//         B int
 
77
//     }
 
78
//     var t T
 
79
//     yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
 
80
//
 
81
// See the documentation of Marshal for the format of tags and a list of
 
82
// supported tag options.
 
83
//
 
84
func Unmarshal(in []byte, out interface{}) (err error) {
 
85
        defer handleErr(&err)
 
86
        d := newDecoder()
 
87
        p := newParser(in)
 
88
        defer p.destroy()
 
89
        node := p.parse()
 
90
        if node != nil {
 
91
                v := reflect.ValueOf(out)
 
92
                if v.Kind() == reflect.Ptr && !v.IsNil() {
 
93
                        v = v.Elem()
 
94
                }
 
95
                d.unmarshal(node, v)
 
96
        }
 
97
        return nil
 
98
}
 
99
 
 
100
// Marshal serializes the value provided into a YAML document. The structure
 
101
// of the generated document will reflect the structure of the value itself.
 
102
// Maps and pointers (to struct, string, int, etc) are accepted as the in value.
 
103
//
 
104
// Struct fields are only unmarshalled if they are exported (have an upper case
 
105
// first letter), and are unmarshalled using the field name lowercased as the
 
106
// default key. Custom keys may be defined via the "yaml" name in the field
 
107
// tag: the content preceding the first comma is used as the key, and the
 
108
// following comma-separated options are used to tweak the marshalling process.
 
109
// Conflicting names result in a runtime error.
 
110
//
 
111
// The field tag format accepted is:
 
112
//
 
113
//     `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
 
114
//
 
115
// The following flags are currently supported:
 
116
//
 
117
//     omitempty    Only include the field if it's not set to the zero
 
118
//                  value for the type or to empty slices or maps.
 
119
//                  Does not apply to zero valued structs.
 
120
//
 
121
//     flow         Marshal using a flow style (useful for structs,
 
122
//                  sequences and maps.
 
123
//
 
124
//     inline       Inline the struct it's applied to, so its fields
 
125
//                  are processed as if they were part of the outer
 
126
//                  struct.
 
127
//
 
128
// In addition, if the key is "-", the field is ignored.
 
129
//
 
130
// For example:
 
131
//
 
132
//     type T struct {
 
133
//         F int "a,omitempty"
 
134
//         B int
 
135
//     }
 
136
//     yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
 
137
//     yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
 
138
//
 
139
func Marshal(in interface{}) (out []byte, err error) {
 
140
        defer handleErr(&err)
 
141
        e := newEncoder()
 
142
        defer e.destroy()
 
143
        e.marshal("", reflect.ValueOf(in))
 
144
        e.finish()
 
145
        out = e.out
 
146
        return
 
147
}
 
148
 
 
149
// --------------------------------------------------------------------------
 
150
// Maintain a mapping of keys to structure field indexes
 
151
 
 
152
// The code in this section was copied from mgo/bson.
 
153
 
 
154
// structInfo holds details for the serialization of fields of
 
155
// a given struct.
 
156
type structInfo struct {
 
157
        FieldsMap  map[string]fieldInfo
 
158
        FieldsList []fieldInfo
 
159
 
 
160
        // InlineMap is the number of the field in the struct that
 
161
        // contains an ,inline map, or -1 if there's none.
 
162
        InlineMap int
 
163
}
 
164
 
 
165
type fieldInfo struct {
 
166
        Key       string
 
167
        Num       int
 
168
        OmitEmpty bool
 
169
        Flow      bool
 
170
 
 
171
        // Inline holds the field index if the field is part of an inlined struct.
 
172
        Inline []int
 
173
}
 
174
 
 
175
var structMap = make(map[reflect.Type]*structInfo)
 
176
var fieldMapMutex sync.RWMutex
 
177
 
 
178
func getStructInfo(st reflect.Type) (*structInfo, error) {
 
179
        fieldMapMutex.RLock()
 
180
        sinfo, found := structMap[st]
 
181
        fieldMapMutex.RUnlock()
 
182
        if found {
 
183
                return sinfo, nil
 
184
        }
 
185
 
 
186
        n := st.NumField()
 
187
        fieldsMap := make(map[string]fieldInfo)
 
188
        fieldsList := make([]fieldInfo, 0, n)
 
189
        inlineMap := -1
 
190
        for i := 0; i != n; i++ {
 
191
                field := st.Field(i)
 
192
                if field.PkgPath != "" {
 
193
                        continue // Private field
 
194
                }
 
195
 
 
196
                info := fieldInfo{Num: i}
 
197
 
 
198
                tag := field.Tag.Get("yaml")
 
199
                if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
 
200
                        tag = string(field.Tag)
 
201
                }
 
202
                if tag == "-" {
 
203
                        continue
 
204
                }
 
205
 
 
206
                inline := false
 
207
                fields := strings.Split(tag, ",")
 
208
                if len(fields) > 1 {
 
209
                        for _, flag := range fields[1:] {
 
210
                                switch flag {
 
211
                                case "omitempty":
 
212
                                        info.OmitEmpty = true
 
213
                                case "flow":
 
214
                                        info.Flow = true
 
215
                                case "inline":
 
216
                                        inline = true
 
217
                                default:
 
218
                                        return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
 
219
                                }
 
220
                        }
 
221
                        tag = fields[0]
 
222
                }
 
223
 
 
224
                if inline {
 
225
                        switch field.Type.Kind() {
 
226
                        // TODO: Implement support for inline maps.
 
227
                        //case reflect.Map:
 
228
                        //      if inlineMap >= 0 {
 
229
                        //              return nil, errors.New("Multiple ,inline maps in struct " + st.String())
 
230
                        //      }
 
231
                        //      if field.Type.Key() != reflect.TypeOf("") {
 
232
                        //              return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
 
233
                        //      }
 
234
                        //      inlineMap = info.Num
 
235
                        case reflect.Struct:
 
236
                                sinfo, err := getStructInfo(field.Type)
 
237
                                if err != nil {
 
238
                                        return nil, err
 
239
                                }
 
240
                                for _, finfo := range sinfo.FieldsList {
 
241
                                        if _, found := fieldsMap[finfo.Key]; found {
 
242
                                                msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
 
243
                                                return nil, errors.New(msg)
 
244
                                        }
 
245
                                        if finfo.Inline == nil {
 
246
                                                finfo.Inline = []int{i, finfo.Num}
 
247
                                        } else {
 
248
                                                finfo.Inline = append([]int{i}, finfo.Inline...)
 
249
                                        }
 
250
                                        fieldsMap[finfo.Key] = finfo
 
251
                                        fieldsList = append(fieldsList, finfo)
 
252
                                }
 
253
                        default:
 
254
                                //return nil, errors.New("Option ,inline needs a struct value or map field")
 
255
                                return nil, errors.New("Option ,inline needs a struct value field")
 
256
                        }
 
257
                        continue
 
258
                }
 
259
 
 
260
                if tag != "" {
 
261
                        info.Key = tag
 
262
                } else {
 
263
                        info.Key = strings.ToLower(field.Name)
 
264
                }
 
265
 
 
266
                if _, found = fieldsMap[info.Key]; found {
 
267
                        msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
 
268
                        return nil, errors.New(msg)
 
269
                }
 
270
 
 
271
                fieldsList = append(fieldsList, info)
 
272
                fieldsMap[info.Key] = info
 
273
        }
 
274
 
 
275
        sinfo = &structInfo{fieldsMap, fieldsList, inlineMap}
 
276
 
 
277
        fieldMapMutex.Lock()
 
278
        structMap[st] = sinfo
 
279
        fieldMapMutex.Unlock()
 
280
        return sinfo, nil
 
281
}
 
282
 
 
283
func isZero(v reflect.Value) bool {
 
284
        switch v.Kind() {
 
285
        case reflect.String:
 
286
                return len(v.String()) == 0
 
287
        case reflect.Interface, reflect.Ptr:
 
288
                return v.IsNil()
 
289
        case reflect.Slice:
 
290
                return v.Len() == 0
 
291
        case reflect.Map:
 
292
                return v.Len() == 0
 
293
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 
294
                return v.Int() == 0
 
295
        case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 
296
                return v.Uint() == 0
 
297
        case reflect.Bool:
 
298
                return !v.Bool()
 
299
        }
 
300
        return false
 
301
}