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