blob: 955cd82af79270a145fa29d2e30b3dd1725a3243 [file] [log] [blame]
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +01001// 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//
Marcel van Lohuizen2156c812018-12-10 16:05:07 +01007package yaml // import "cuelang.org/go/internal/third_party/yaml"
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +01008
9import (
10 "errors"
11 "fmt"
12 "io"
13 "reflect"
Marcel van Lohuizen2156c812018-12-10 16:05:07 +010014 "strconv"
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +010015 "strings"
16 "sync"
Marcel van Lohuizen2156c812018-12-10 16:05:07 +010017
18 "cuelang.org/go/cue/ast"
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +010019)
20
21// MapSlice encodes and decodes as a YAML map.
22// The order of keys is preserved when encoding and decoding.
23type MapSlice []MapItem
24
25// MapItem is an item in a MapSlice.
26type MapItem struct {
27 Key, Value interface{}
28}
29
30// The Unmarshaler interface may be implemented by types to customize their
31// behavior when being unmarshaled from a YAML document. The UnmarshalYAML
32// method receives a function that may be called to unmarshal the original
33// YAML value into a field or variable. It is safe to call the unmarshal
34// function parameter more than once if necessary.
35type Unmarshaler interface {
36 UnmarshalYAML(unmarshal func(interface{}) error) error
37}
38
39// The Marshaler interface may be implemented by types to customize their
40// behavior when being marshaled into a YAML document. The returned value
41// is marshaled in place of the original value implementing Marshaler.
42//
43// If an error is returned by MarshalYAML, the marshaling procedure stops
44// and returns with the provided error.
45type Marshaler interface {
46 MarshalYAML() (interface{}, error)
47}
48
49// Unmarshal decodes the first document found within the in byte slice
50// and assigns decoded values into the out value.
51//
52// Maps and pointers (to a struct, string, int, etc) are accepted as out
53// values. If an internal pointer within a struct is not initialized,
54// the yaml package will initialize it if necessary for unmarshalling
55// the provided data. The out parameter must not be nil.
56//
57// The type of the decoded values should be compatible with the respective
58// values in out. If one or more values cannot be decoded due to a type
59// mismatches, decoding continues partially until the end of the YAML
60// content, and a *yaml.TypeError is returned with details for all
61// missed values.
62//
63// Struct fields are only unmarshalled if they are exported (have an
64// upper case first letter), and are unmarshalled using the field name
65// lowercased as the default key. Custom keys may be defined via the
66// "yaml" name in the field tag: the content preceding the first comma
67// is used as the key, and the following comma-separated options are
68// used to tweak the marshalling process (see Marshal).
69// Conflicting names result in a runtime error.
70//
71// For example:
72//
73// type T struct {
74// F int `yaml:"a,omitempty"`
75// B int
76// }
77// var t T
78// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
79//
80// See the documentation of Marshal for the format of tags and a list of
81// supported tag options.
82//
Marcel van Lohuizen3c7c40b2019-05-22 11:31:27 -040083func Unmarshal(filename string, in []byte) (expr ast.Expr, err error) {
84 return unmarshal(filename, in)
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +010085}
86
87// A Decorder reads and decodes YAML values from an input stream.
88type Decoder struct {
89 strict bool
90 parser *parser
91}
92
93// NewDecoder returns a new decoder that reads from r.
94//
95// The decoder introduces its own buffering and may read
96// data from r beyond the YAML values requested.
Marcel van Lohuizen3c7c40b2019-05-22 11:31:27 -040097func NewDecoder(filename string, r io.Reader) (*Decoder, error) {
98 d, err := newParser(filename, r)
Marcel van Lohuizen2156c812018-12-10 16:05:07 +010099 if err != nil {
100 return nil, err
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100101 }
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100102 return &Decoder{parser: d}, nil
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100103}
104
105// Decode reads the next YAML-encoded value from its input
106// and stores it in the value pointed to by v.
107//
108// See the documentation for Unmarshal for details about the
109// conversion of YAML into a Go value.
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100110func (dec *Decoder) Decode() (expr ast.Expr, err error) {
111 d := newDecoder(dec.parser)
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100112 defer handleErr(&err)
113 node := dec.parser.parse()
114 if node == nil {
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100115 return nil, io.EOF
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100116 }
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100117 expr = d.unmarshal(node)
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100118 if len(d.terrors) > 0 {
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100119 return nil, &TypeError{d.terrors}
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100120 }
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100121 return expr, nil
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100122}
123
Marcel van Lohuizen3c7c40b2019-05-22 11:31:27 -0400124func unmarshal(filename string, in []byte) (expr ast.Expr, err error) {
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100125 defer handleErr(&err)
Marcel van Lohuizen3c7c40b2019-05-22 11:31:27 -0400126 p, err := newParser(filename, in)
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100127 if err != nil {
128 return nil, err
129 }
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100130 defer p.destroy()
131 node := p.parse()
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100132 d := newDecoder(p)
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100133 if node != nil {
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100134 expr = d.unmarshal(node)
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100135 }
136 if len(d.terrors) > 0 {
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100137 return nil, &TypeError{d.terrors}
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100138 }
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100139 return expr, nil
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100140}
141
142func handleErr(err *error) {
143 if v := recover(); v != nil {
144 if e, ok := v.(yamlError); ok {
145 *err = e.err
146 } else {
147 panic(v)
148 }
149 }
150}
151
152type yamlError struct {
153 err error
154}
155
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100156func (p *parser) failf(line int, format string, args ...interface{}) {
157 where := p.parser.filename + ":"
158 line++
159 where += strconv.Itoa(line) + ": "
160 panic(yamlError{fmt.Errorf(where+format, args...)})
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100161}
162
163// A TypeError is returned by Unmarshal when one or more fields in
164// the YAML document cannot be properly decoded into the requested
165// types. When this error is returned, the value is still
166// unmarshaled partially.
167type TypeError struct {
168 Errors []string
169}
170
171func (e *TypeError) Error() string {
172 return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))
173}
174
175// --------------------------------------------------------------------------
176// Maintain a mapping of keys to structure field indexes
177
178// The code in this section was copied from mgo/bson.
179
180// structInfo holds details for the serialization of fields of
181// a given struct.
182type structInfo struct {
183 FieldsMap map[string]fieldInfo
184 FieldsList []fieldInfo
185
186 // InlineMap is the number of the field in the struct that
187 // contains an ,inline map, or -1 if there's none.
188 InlineMap int
189}
190
191type fieldInfo struct {
192 Key string
193 Num int
194 OmitEmpty bool
195 Flow bool
196 // Id holds the unique field identifier, so we can cheaply
197 // check for field duplicates without maintaining an extra map.
198 Id int
199
200 // Inline holds the field index if the field is part of an inlined struct.
201 Inline []int
202}
203
204var structMap = make(map[reflect.Type]*structInfo)
205var fieldMapMutex sync.RWMutex
206
207func getStructInfo(st reflect.Type) (*structInfo, error) {
208 fieldMapMutex.RLock()
209 sinfo, found := structMap[st]
210 fieldMapMutex.RUnlock()
211 if found {
212 return sinfo, nil
213 }
214
215 n := st.NumField()
216 fieldsMap := make(map[string]fieldInfo)
217 fieldsList := make([]fieldInfo, 0, n)
218 inlineMap := -1
219 for i := 0; i != n; i++ {
220 field := st.Field(i)
221 if field.PkgPath != "" && !field.Anonymous {
222 continue // Private field
223 }
224
225 info := fieldInfo{Num: i}
226
227 tag := field.Tag.Get("yaml")
228 if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
229 tag = string(field.Tag)
230 }
231 if tag == "-" {
232 continue
233 }
234
235 inline := false
236 fields := strings.Split(tag, ",")
237 if len(fields) > 1 {
238 for _, flag := range fields[1:] {
239 switch flag {
240 case "omitempty":
241 info.OmitEmpty = true
242 case "flow":
243 info.Flow = true
244 case "inline":
245 inline = true
246 default:
247 return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
248 }
249 }
250 tag = fields[0]
251 }
252
253 if inline {
254 switch field.Type.Kind() {
255 case reflect.Map:
256 if inlineMap >= 0 {
257 return nil, errors.New("Multiple ,inline maps in struct " + st.String())
258 }
259 if field.Type.Key() != reflect.TypeOf("") {
260 return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
261 }
262 inlineMap = info.Num
263 case reflect.Struct:
264 sinfo, err := getStructInfo(field.Type)
265 if err != nil {
266 return nil, err
267 }
268 for _, finfo := range sinfo.FieldsList {
269 if _, found := fieldsMap[finfo.Key]; found {
270 msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
271 return nil, errors.New(msg)
272 }
273 if finfo.Inline == nil {
274 finfo.Inline = []int{i, finfo.Num}
275 } else {
276 finfo.Inline = append([]int{i}, finfo.Inline...)
277 }
278 finfo.Id = len(fieldsList)
279 fieldsMap[finfo.Key] = finfo
280 fieldsList = append(fieldsList, finfo)
281 }
282 default:
283 //return nil, errors.New("Option ,inline needs a struct value or map field")
284 return nil, errors.New("Option ,inline needs a struct value field")
285 }
286 continue
287 }
288
289 if tag != "" {
290 info.Key = tag
291 } else {
292 info.Key = strings.ToLower(field.Name)
293 }
294
295 if _, found = fieldsMap[info.Key]; found {
296 msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
297 return nil, errors.New(msg)
298 }
299
300 info.Id = len(fieldsList)
301 fieldsList = append(fieldsList, info)
302 fieldsMap[info.Key] = info
303 }
304
305 sinfo = &structInfo{
306 FieldsMap: fieldsMap,
307 FieldsList: fieldsList,
308 InlineMap: inlineMap,
309 }
310
311 fieldMapMutex.Lock()
312 structMap[st] = sinfo
313 fieldMapMutex.Unlock()
314 return sinfo, nil
315}
316
317// IsZeroer is used to check whether an object is zero to
318// determine whether it should be omitted when marshaling
319// with the omitempty flag. One notable implementation
320// is time.Time.
321type IsZeroer interface {
322 IsZero() bool
323}
324
325func isZero(v reflect.Value) bool {
326 kind := v.Kind()
327 if z, ok := v.Interface().(IsZeroer); ok {
328 if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() {
329 return true
330 }
331 return z.IsZero()
332 }
333 switch kind {
334 case reflect.String:
335 return len(v.String()) == 0
336 case reflect.Interface, reflect.Ptr:
337 return v.IsNil()
338 case reflect.Slice:
339 return v.Len() == 0
340 case reflect.Map:
341 return v.Len() == 0
342 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
343 return v.Int() == 0
344 case reflect.Float32, reflect.Float64:
345 return v.Float() == 0
346 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
347 return v.Uint() == 0
348 case reflect.Bool:
349 return !v.Bool()
350 case reflect.Struct:
351 vt := v.Type()
352 for i := v.NumField() - 1; i >= 0; i-- {
353 if vt.Field(i).PkgPath != "" {
354 continue // Private field
355 }
356 if !isZero(v.Field(i)) {
357 return false
358 }
359 }
360 return true
361 }
362 return false
363}