blob: 517761459c0e0e54fca6ea7f4a46a39eb0a63e50 [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 {
Marcel van Lohuizenc615c912020-07-07 13:28:07 +020089 strict bool
90 firstDone bool
91 parser *parser
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +010092}
93
94// NewDecoder returns a new decoder that reads from r.
95//
96// The decoder introduces its own buffering and may read
97// data from r beyond the YAML values requested.
Marcel van Lohuizen4e5b69a2019-07-14 21:24:00 +020098func NewDecoder(filename string, src interface{}) (*Decoder, error) {
99 d, err := newParser(filename, src)
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100100 if err != nil {
101 return nil, err
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100102 }
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100103 return &Decoder{parser: d}, nil
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100104}
105
Marcel van Lohuizen4697b782019-07-12 22:19:43 +0200106// Decode reads the next YAML-encoded value from its input and stores it in the
107// value pointed to by v. It returns io.EOF if there are no more value in the
108// stream.
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100109//
Marcel van Lohuizen4697b782019-07-12 22:19:43 +0200110// See the documentation for Unmarshal for details about the conversion of YAML
111// into a Go value.
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100112func (dec *Decoder) Decode() (expr ast.Expr, err error) {
113 d := newDecoder(dec.parser)
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100114 defer handleErr(&err)
115 node := dec.parser.parse()
116 if node == nil {
Marcel van Lohuizenc615c912020-07-07 13:28:07 +0200117 if !dec.firstDone {
118 expr = ast.NewNull()
119 }
120 return expr, io.EOF
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100121 }
Marcel van Lohuizenc615c912020-07-07 13:28:07 +0200122 dec.firstDone = true
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100123 expr = d.unmarshal(node)
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100124 if len(d.terrors) > 0 {
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100125 return nil, &TypeError{d.terrors}
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100126 }
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100127 return expr, nil
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100128}
129
Marcel van Lohuizen3c7c40b2019-05-22 11:31:27 -0400130func unmarshal(filename string, in []byte) (expr ast.Expr, err error) {
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100131 defer handleErr(&err)
Marcel van Lohuizen3c7c40b2019-05-22 11:31:27 -0400132 p, err := newParser(filename, in)
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100133 if err != nil {
134 return nil, err
135 }
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100136 defer p.destroy()
137 node := p.parse()
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100138 d := newDecoder(p)
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100139 if node != nil {
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100140 expr = d.unmarshal(node)
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100141 }
142 if len(d.terrors) > 0 {
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100143 return nil, &TypeError{d.terrors}
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100144 }
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100145 return expr, nil
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100146}
147
148func handleErr(err *error) {
149 if v := recover(); v != nil {
150 if e, ok := v.(yamlError); ok {
151 *err = e.err
152 } else {
153 panic(v)
154 }
155 }
156}
157
158type yamlError struct {
159 err error
160}
161
Marcel van Lohuizen2156c812018-12-10 16:05:07 +0100162func (p *parser) failf(line int, format string, args ...interface{}) {
163 where := p.parser.filename + ":"
164 line++
165 where += strconv.Itoa(line) + ": "
166 panic(yamlError{fmt.Errorf(where+format, args...)})
Marcel van Lohuizen07ee2ab2018-12-10 15:57:15 +0100167}
168
169// A TypeError is returned by Unmarshal when one or more fields in
170// the YAML document cannot be properly decoded into the requested
171// types. When this error is returned, the value is still
172// unmarshaled partially.
173type TypeError struct {
174 Errors []string
175}
176
177func (e *TypeError) Error() string {
178 return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))
179}
180
181// --------------------------------------------------------------------------
182// Maintain a mapping of keys to structure field indexes
183
184// The code in this section was copied from mgo/bson.
185
186// structInfo holds details for the serialization of fields of
187// a given struct.
188type structInfo struct {
189 FieldsMap map[string]fieldInfo
190 FieldsList []fieldInfo
191
192 // InlineMap is the number of the field in the struct that
193 // contains an ,inline map, or -1 if there's none.
194 InlineMap int
195}
196
197type fieldInfo struct {
198 Key string
199 Num int
200 OmitEmpty bool
201 Flow bool
202 // Id holds the unique field identifier, so we can cheaply
203 // check for field duplicates without maintaining an extra map.
204 Id int
205
206 // Inline holds the field index if the field is part of an inlined struct.
207 Inline []int
208}
209
210var structMap = make(map[reflect.Type]*structInfo)
211var fieldMapMutex sync.RWMutex
212
213func getStructInfo(st reflect.Type) (*structInfo, error) {
214 fieldMapMutex.RLock()
215 sinfo, found := structMap[st]
216 fieldMapMutex.RUnlock()
217 if found {
218 return sinfo, nil
219 }
220
221 n := st.NumField()
222 fieldsMap := make(map[string]fieldInfo)
223 fieldsList := make([]fieldInfo, 0, n)
224 inlineMap := -1
225 for i := 0; i != n; i++ {
226 field := st.Field(i)
227 if field.PkgPath != "" && !field.Anonymous {
228 continue // Private field
229 }
230
231 info := fieldInfo{Num: i}
232
233 tag := field.Tag.Get("yaml")
234 if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
235 tag = string(field.Tag)
236 }
237 if tag == "-" {
238 continue
239 }
240
241 inline := false
242 fields := strings.Split(tag, ",")
243 if len(fields) > 1 {
244 for _, flag := range fields[1:] {
245 switch flag {
246 case "omitempty":
247 info.OmitEmpty = true
248 case "flow":
249 info.Flow = true
250 case "inline":
251 inline = true
252 default:
253 return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
254 }
255 }
256 tag = fields[0]
257 }
258
259 if inline {
260 switch field.Type.Kind() {
261 case reflect.Map:
262 if inlineMap >= 0 {
263 return nil, errors.New("Multiple ,inline maps in struct " + st.String())
264 }
265 if field.Type.Key() != reflect.TypeOf("") {
266 return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
267 }
268 inlineMap = info.Num
269 case reflect.Struct:
270 sinfo, err := getStructInfo(field.Type)
271 if err != nil {
272 return nil, err
273 }
274 for _, finfo := range sinfo.FieldsList {
275 if _, found := fieldsMap[finfo.Key]; found {
276 msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
277 return nil, errors.New(msg)
278 }
279 if finfo.Inline == nil {
280 finfo.Inline = []int{i, finfo.Num}
281 } else {
282 finfo.Inline = append([]int{i}, finfo.Inline...)
283 }
284 finfo.Id = len(fieldsList)
285 fieldsMap[finfo.Key] = finfo
286 fieldsList = append(fieldsList, finfo)
287 }
288 default:
289 //return nil, errors.New("Option ,inline needs a struct value or map field")
290 return nil, errors.New("Option ,inline needs a struct value field")
291 }
292 continue
293 }
294
295 if tag != "" {
296 info.Key = tag
297 } else {
298 info.Key = strings.ToLower(field.Name)
299 }
300
301 if _, found = fieldsMap[info.Key]; found {
302 msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
303 return nil, errors.New(msg)
304 }
305
306 info.Id = len(fieldsList)
307 fieldsList = append(fieldsList, info)
308 fieldsMap[info.Key] = info
309 }
310
311 sinfo = &structInfo{
312 FieldsMap: fieldsMap,
313 FieldsList: fieldsList,
314 InlineMap: inlineMap,
315 }
316
317 fieldMapMutex.Lock()
318 structMap[st] = sinfo
319 fieldMapMutex.Unlock()
320 return sinfo, nil
321}
322
323// IsZeroer is used to check whether an object is zero to
324// determine whether it should be omitted when marshaling
325// with the omitempty flag. One notable implementation
326// is time.Time.
327type IsZeroer interface {
328 IsZero() bool
329}
330
331func isZero(v reflect.Value) bool {
332 kind := v.Kind()
333 if z, ok := v.Interface().(IsZeroer); ok {
334 if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() {
335 return true
336 }
337 return z.IsZero()
338 }
339 switch kind {
340 case reflect.String:
341 return len(v.String()) == 0
342 case reflect.Interface, reflect.Ptr:
343 return v.IsNil()
344 case reflect.Slice:
345 return v.Len() == 0
346 case reflect.Map:
347 return v.Len() == 0
348 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
349 return v.Int() == 0
350 case reflect.Float32, reflect.Float64:
351 return v.Float() == 0
352 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
353 return v.Uint() == 0
354 case reflect.Bool:
355 return !v.Bool()
356 case reflect.Struct:
357 vt := v.Type()
358 for i := v.NumField() - 1; i >= 0; i-- {
359 if vt.Field(i).PkgPath != "" {
360 continue // Private field
361 }
362 if !isZero(v.Field(i)) {
363 return false
364 }
365 }
366 return true
367 }
368 return false
369}