1 |
yakumo_izuru |
1.1 |
// |
2 |
|
|
// Copyright (c) 2011-2019 Canonical Ltd |
3 |
|
|
// |
4 |
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); |
5 |
|
|
// you may not use this file except in compliance with the License. |
6 |
|
|
// You may obtain a copy of the License at |
7 |
|
|
// |
8 |
|
|
// http://www.apache.org/licenses/LICENSE-2.0 |
9 |
|
|
// |
10 |
|
|
// Unless required by applicable law or agreed to in writing, software |
11 |
|
|
// distributed under the License is distributed on an "AS IS" BASIS, |
12 |
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 |
|
|
// See the License for the specific language governing permissions and |
14 |
|
|
// limitations under the License. |
15 |
|
|
|
16 |
|
|
// Package yaml implements YAML support for the Go language. |
17 |
|
|
// |
18 |
|
|
// Source code and other details for the project are available at GitHub: |
19 |
|
|
// |
20 |
|
|
// https://github.com/go-yaml/yaml |
21 |
|
|
// |
22 |
|
|
package yaml |
23 |
|
|
|
24 |
|
|
import ( |
25 |
|
|
"errors" |
26 |
|
|
"fmt" |
27 |
|
|
"io" |
28 |
|
|
"reflect" |
29 |
|
|
"strings" |
30 |
|
|
"sync" |
31 |
|
|
"unicode/utf8" |
32 |
|
|
) |
33 |
|
|
|
34 |
|
|
// The Unmarshaler interface may be implemented by types to customize their |
35 |
|
|
// behavior when being unmarshaled from a YAML document. |
36 |
|
|
type Unmarshaler interface { |
37 |
|
|
UnmarshalYAML(value *Node) error |
38 |
|
|
} |
39 |
|
|
|
40 |
|
|
type obsoleteUnmarshaler interface { |
41 |
|
|
UnmarshalYAML(unmarshal func(interface{}) error) error |
42 |
|
|
} |
43 |
|
|
|
44 |
|
|
// The Marshaler interface may be implemented by types to customize their |
45 |
|
|
// behavior when being marshaled into a YAML document. The returned value |
46 |
|
|
// is marshaled in place of the original value implementing Marshaler. |
47 |
|
|
// |
48 |
|
|
// If an error is returned by MarshalYAML, the marshaling procedure stops |
49 |
|
|
// and returns with the provided error. |
50 |
|
|
type Marshaler interface { |
51 |
|
|
MarshalYAML() (interface{}, error) |
52 |
|
|
} |
53 |
|
|
|
54 |
|
|
// Unmarshal decodes the first document found within the in byte slice |
55 |
|
|
// and assigns decoded values into the out value. |
56 |
|
|
// |
57 |
|
|
// Maps and pointers (to a struct, string, int, etc) are accepted as out |
58 |
|
|
// values. If an internal pointer within a struct is not initialized, |
59 |
|
|
// the yaml package will initialize it if necessary for unmarshalling |
60 |
|
|
// the provided data. The out parameter must not be nil. |
61 |
|
|
// |
62 |
|
|
// The type of the decoded values should be compatible with the respective |
63 |
|
|
// values in out. If one or more values cannot be decoded due to a type |
64 |
|
|
// mismatches, decoding continues partially until the end of the YAML |
65 |
|
|
// content, and a *yaml.TypeError is returned with details for all |
66 |
|
|
// missed values. |
67 |
|
|
// |
68 |
|
|
// Struct fields are only unmarshalled if they are exported (have an |
69 |
|
|
// upper case first letter), and are unmarshalled using the field name |
70 |
|
|
// lowercased as the default key. Custom keys may be defined via the |
71 |
|
|
// "yaml" name in the field tag: the content preceding the first comma |
72 |
|
|
// is used as the key, and the following comma-separated options are |
73 |
|
|
// used to tweak the marshalling process (see Marshal). |
74 |
|
|
// Conflicting names result in a runtime error. |
75 |
|
|
// |
76 |
|
|
// For example: |
77 |
|
|
// |
78 |
|
|
// type T struct { |
79 |
|
|
// F int `yaml:"a,omitempty"` |
80 |
|
|
// B int |
81 |
|
|
// } |
82 |
|
|
// var t T |
83 |
|
|
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) |
84 |
|
|
// |
85 |
|
|
// See the documentation of Marshal for the format of tags and a list of |
86 |
|
|
// supported tag options. |
87 |
|
|
// |
88 |
|
|
func Unmarshal(in []byte, out interface{}) (err error) { |
89 |
|
|
return unmarshal(in, out, false) |
90 |
|
|
} |
91 |
|
|
|
92 |
|
|
// A Decoder reads and decodes YAML values from an input stream. |
93 |
|
|
type Decoder struct { |
94 |
|
|
parser *parser |
95 |
|
|
knownFields bool |
96 |
|
|
} |
97 |
|
|
|
98 |
|
|
// NewDecoder returns a new decoder that reads from r. |
99 |
|
|
// |
100 |
|
|
// The decoder introduces its own buffering and may read |
101 |
|
|
// data from r beyond the YAML values requested. |
102 |
|
|
func NewDecoder(r io.Reader) *Decoder { |
103 |
|
|
return &Decoder{ |
104 |
|
|
parser: newParserFromReader(r), |
105 |
|
|
} |
106 |
|
|
} |
107 |
|
|
|
108 |
|
|
// KnownFields ensures that the keys in decoded mappings to |
109 |
|
|
// exist as fields in the struct being decoded into. |
110 |
|
|
func (dec *Decoder) KnownFields(enable bool) { |
111 |
|
|
dec.knownFields = enable |
112 |
|
|
} |
113 |
|
|
|
114 |
|
|
// Decode reads the next YAML-encoded value from its input |
115 |
|
|
// and stores it in the value pointed to by v. |
116 |
|
|
// |
117 |
|
|
// See the documentation for Unmarshal for details about the |
118 |
|
|
// conversion of YAML into a Go value. |
119 |
|
|
func (dec *Decoder) Decode(v interface{}) (err error) { |
120 |
|
|
d := newDecoder() |
121 |
|
|
d.knownFields = dec.knownFields |
122 |
|
|
defer handleErr(&err) |
123 |
|
|
node := dec.parser.parse() |
124 |
|
|
if node == nil { |
125 |
|
|
return io.EOF |
126 |
|
|
} |
127 |
|
|
out := reflect.ValueOf(v) |
128 |
|
|
if out.Kind() == reflect.Ptr && !out.IsNil() { |
129 |
|
|
out = out.Elem() |
130 |
|
|
} |
131 |
|
|
d.unmarshal(node, out) |
132 |
|
|
if len(d.terrors) > 0 { |
133 |
|
|
return &TypeError{d.terrors} |
134 |
|
|
} |
135 |
|
|
return nil |
136 |
|
|
} |
137 |
|
|
|
138 |
|
|
// Decode decodes the node and stores its data into the value pointed to by v. |
139 |
|
|
// |
140 |
|
|
// See the documentation for Unmarshal for details about the |
141 |
|
|
// conversion of YAML into a Go value. |
142 |
|
|
func (n *Node) Decode(v interface{}) (err error) { |
143 |
|
|
d := newDecoder() |
144 |
|
|
defer handleErr(&err) |
145 |
|
|
out := reflect.ValueOf(v) |
146 |
|
|
if out.Kind() == reflect.Ptr && !out.IsNil() { |
147 |
|
|
out = out.Elem() |
148 |
|
|
} |
149 |
|
|
d.unmarshal(n, out) |
150 |
|
|
if len(d.terrors) > 0 { |
151 |
|
|
return &TypeError{d.terrors} |
152 |
|
|
} |
153 |
|
|
return nil |
154 |
|
|
} |
155 |
|
|
|
156 |
|
|
func unmarshal(in []byte, out interface{}, strict bool) (err error) { |
157 |
|
|
defer handleErr(&err) |
158 |
|
|
d := newDecoder() |
159 |
|
|
p := newParser(in) |
160 |
|
|
defer p.destroy() |
161 |
|
|
node := p.parse() |
162 |
|
|
if node != nil { |
163 |
|
|
v := reflect.ValueOf(out) |
164 |
|
|
if v.Kind() == reflect.Ptr && !v.IsNil() { |
165 |
|
|
v = v.Elem() |
166 |
|
|
} |
167 |
|
|
d.unmarshal(node, v) |
168 |
|
|
} |
169 |
|
|
if len(d.terrors) > 0 { |
170 |
|
|
return &TypeError{d.terrors} |
171 |
|
|
} |
172 |
|
|
return nil |
173 |
|
|
} |
174 |
|
|
|
175 |
|
|
// Marshal serializes the value provided into a YAML document. The structure |
176 |
|
|
// of the generated document will reflect the structure of the value itself. |
177 |
|
|
// Maps and pointers (to struct, string, int, etc) are accepted as the in value. |
178 |
|
|
// |
179 |
|
|
// Struct fields are only marshalled if they are exported (have an upper case |
180 |
|
|
// first letter), and are marshalled using the field name lowercased as the |
181 |
|
|
// default key. Custom keys may be defined via the "yaml" name in the field |
182 |
|
|
// tag: the content preceding the first comma is used as the key, and the |
183 |
|
|
// following comma-separated options are used to tweak the marshalling process. |
184 |
|
|
// Conflicting names result in a runtime error. |
185 |
|
|
// |
186 |
|
|
// The field tag format accepted is: |
187 |
|
|
// |
188 |
|
|
// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)` |
189 |
|
|
// |
190 |
|
|
// The following flags are currently supported: |
191 |
|
|
// |
192 |
|
|
// omitempty Only include the field if it's not set to the zero |
193 |
|
|
// value for the type or to empty slices or maps. |
194 |
|
|
// Zero valued structs will be omitted if all their public |
195 |
|
|
// fields are zero, unless they implement an IsZero |
196 |
|
|
// method (see the IsZeroer interface type), in which |
197 |
|
|
// case the field will be excluded if IsZero returns true. |
198 |
|
|
// |
199 |
|
|
// flow Marshal using a flow style (useful for structs, |
200 |
|
|
// sequences and maps). |
201 |
|
|
// |
202 |
|
|
// inline Inline the field, which must be a struct or a map, |
203 |
|
|
// causing all of its fields or keys to be processed as if |
204 |
|
|
// they were part of the outer struct. For maps, keys must |
205 |
|
|
// not conflict with the yaml keys of other struct fields. |
206 |
|
|
// |
207 |
|
|
// In addition, if the key is "-", the field is ignored. |
208 |
|
|
// |
209 |
|
|
// For example: |
210 |
|
|
// |
211 |
|
|
// type T struct { |
212 |
|
|
// F int `yaml:"a,omitempty"` |
213 |
|
|
// B int |
214 |
|
|
// } |
215 |
|
|
// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" |
216 |
|
|
// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" |
217 |
|
|
// |
218 |
|
|
func Marshal(in interface{}) (out []byte, err error) { |
219 |
|
|
defer handleErr(&err) |
220 |
|
|
e := newEncoder() |
221 |
|
|
defer e.destroy() |
222 |
|
|
e.marshalDoc("", reflect.ValueOf(in)) |
223 |
|
|
e.finish() |
224 |
|
|
out = e.out |
225 |
|
|
return |
226 |
|
|
} |
227 |
|
|
|
228 |
|
|
// An Encoder writes YAML values to an output stream. |
229 |
|
|
type Encoder struct { |
230 |
|
|
encoder *encoder |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
// NewEncoder returns a new encoder that writes to w. |
234 |
|
|
// The Encoder should be closed after use to flush all data |
235 |
|
|
// to w. |
236 |
|
|
func NewEncoder(w io.Writer) *Encoder { |
237 |
|
|
return &Encoder{ |
238 |
|
|
encoder: newEncoderWithWriter(w), |
239 |
|
|
} |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
// Encode writes the YAML encoding of v to the stream. |
243 |
|
|
// If multiple items are encoded to the stream, the |
244 |
|
|
// second and subsequent document will be preceded |
245 |
|
|
// with a "---" document separator, but the first will not. |
246 |
|
|
// |
247 |
|
|
// See the documentation for Marshal for details about the conversion of Go |
248 |
|
|
// values to YAML. |
249 |
|
|
func (e *Encoder) Encode(v interface{}) (err error) { |
250 |
|
|
defer handleErr(&err) |
251 |
|
|
e.encoder.marshalDoc("", reflect.ValueOf(v)) |
252 |
|
|
return nil |
253 |
|
|
} |
254 |
|
|
|
255 |
|
|
// Encode encodes value v and stores its representation in n. |
256 |
|
|
// |
257 |
|
|
// See the documentation for Marshal for details about the |
258 |
|
|
// conversion of Go values into YAML. |
259 |
|
|
func (n *Node) Encode(v interface{}) (err error) { |
260 |
|
|
defer handleErr(&err) |
261 |
|
|
e := newEncoder() |
262 |
|
|
defer e.destroy() |
263 |
|
|
e.marshalDoc("", reflect.ValueOf(v)) |
264 |
|
|
e.finish() |
265 |
|
|
p := newParser(e.out) |
266 |
|
|
p.textless = true |
267 |
|
|
defer p.destroy() |
268 |
|
|
doc := p.parse() |
269 |
|
|
*n = *doc.Content[0] |
270 |
|
|
return nil |
271 |
|
|
} |
272 |
|
|
|
273 |
|
|
// SetIndent changes the used indentation used when encoding. |
274 |
|
|
func (e *Encoder) SetIndent(spaces int) { |
275 |
|
|
if spaces < 0 { |
276 |
|
|
panic("yaml: cannot indent to a negative number of spaces") |
277 |
|
|
} |
278 |
|
|
e.encoder.indent = spaces |
279 |
|
|
} |
280 |
|
|
|
281 |
|
|
// Close closes the encoder by writing any remaining data. |
282 |
|
|
// It does not write a stream terminating string "...". |
283 |
|
|
func (e *Encoder) Close() (err error) { |
284 |
|
|
defer handleErr(&err) |
285 |
|
|
e.encoder.finish() |
286 |
|
|
return nil |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
func handleErr(err *error) { |
290 |
|
|
if v := recover(); v != nil { |
291 |
|
|
if e, ok := v.(yamlError); ok { |
292 |
|
|
*err = e.err |
293 |
|
|
} else { |
294 |
|
|
panic(v) |
295 |
|
|
} |
296 |
|
|
} |
297 |
|
|
} |
298 |
|
|
|
299 |
|
|
type yamlError struct { |
300 |
|
|
err error |
301 |
|
|
} |
302 |
|
|
|
303 |
|
|
func fail(err error) { |
304 |
|
|
panic(yamlError{err}) |
305 |
|
|
} |
306 |
|
|
|
307 |
|
|
func failf(format string, args ...interface{}) { |
308 |
|
|
panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
// A TypeError is returned by Unmarshal when one or more fields in |
312 |
|
|
// the YAML document cannot be properly decoded into the requested |
313 |
|
|
// types. When this error is returned, the value is still |
314 |
|
|
// unmarshaled partially. |
315 |
|
|
type TypeError struct { |
316 |
|
|
Errors []string |
317 |
|
|
} |
318 |
|
|
|
319 |
|
|
func (e *TypeError) Error() string { |
320 |
|
|
return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) |
321 |
|
|
} |
322 |
|
|
|
323 |
|
|
type Kind uint32 |
324 |
|
|
|
325 |
|
|
const ( |
326 |
|
|
DocumentNode Kind = 1 << iota |
327 |
|
|
SequenceNode |
328 |
|
|
MappingNode |
329 |
|
|
ScalarNode |
330 |
|
|
AliasNode |
331 |
|
|
) |
332 |
|
|
|
333 |
|
|
type Style uint32 |
334 |
|
|
|
335 |
|
|
const ( |
336 |
|
|
TaggedStyle Style = 1 << iota |
337 |
|
|
DoubleQuotedStyle |
338 |
|
|
SingleQuotedStyle |
339 |
|
|
LiteralStyle |
340 |
|
|
FoldedStyle |
341 |
|
|
FlowStyle |
342 |
|
|
) |
343 |
|
|
|
344 |
|
|
// Node represents an element in the YAML document hierarchy. While documents |
345 |
|
|
// are typically encoded and decoded into higher level types, such as structs |
346 |
|
|
// and maps, Node is an intermediate representation that allows detailed |
347 |
|
|
// control over the content being decoded or encoded. |
348 |
|
|
// |
349 |
|
|
// It's worth noting that although Node offers access into details such as |
350 |
|
|
// line numbers, colums, and comments, the content when re-encoded will not |
351 |
|
|
// have its original textual representation preserved. An effort is made to |
352 |
|
|
// render the data plesantly, and to preserve comments near the data they |
353 |
|
|
// describe, though. |
354 |
|
|
// |
355 |
|
|
// Values that make use of the Node type interact with the yaml package in the |
356 |
|
|
// same way any other type would do, by encoding and decoding yaml data |
357 |
|
|
// directly or indirectly into them. |
358 |
|
|
// |
359 |
|
|
// For example: |
360 |
|
|
// |
361 |
|
|
// var person struct { |
362 |
|
|
// Name string |
363 |
|
|
// Address yaml.Node |
364 |
|
|
// } |
365 |
|
|
// err := yaml.Unmarshal(data, &person) |
366 |
|
|
// |
367 |
|
|
// Or by itself: |
368 |
|
|
// |
369 |
|
|
// var person Node |
370 |
|
|
// err := yaml.Unmarshal(data, &person) |
371 |
|
|
// |
372 |
|
|
type Node struct { |
373 |
|
|
// Kind defines whether the node is a document, a mapping, a sequence, |
374 |
|
|
// a scalar value, or an alias to another node. The specific data type of |
375 |
|
|
// scalar nodes may be obtained via the ShortTag and LongTag methods. |
376 |
|
|
Kind Kind |
377 |
|
|
|
378 |
|
|
// Style allows customizing the apperance of the node in the tree. |
379 |
|
|
Style Style |
380 |
|
|
|
381 |
|
|
// Tag holds the YAML tag defining the data type for the value. |
382 |
|
|
// When decoding, this field will always be set to the resolved tag, |
383 |
|
|
// even when it wasn't explicitly provided in the YAML content. |
384 |
|
|
// When encoding, if this field is unset the value type will be |
385 |
|
|
// implied from the node properties, and if it is set, it will only |
386 |
|
|
// be serialized into the representation if TaggedStyle is used or |
387 |
|
|
// the implicit tag diverges from the provided one. |
388 |
|
|
Tag string |
389 |
|
|
|
390 |
|
|
// Value holds the unescaped and unquoted represenation of the value. |
391 |
|
|
Value string |
392 |
|
|
|
393 |
|
|
// Anchor holds the anchor name for this node, which allows aliases to point to it. |
394 |
|
|
Anchor string |
395 |
|
|
|
396 |
|
|
// Alias holds the node that this alias points to. Only valid when Kind is AliasNode. |
397 |
|
|
Alias *Node |
398 |
|
|
|
399 |
|
|
// Content holds contained nodes for documents, mappings, and sequences. |
400 |
|
|
Content []*Node |
401 |
|
|
|
402 |
|
|
// HeadComment holds any comments in the lines preceding the node and |
403 |
|
|
// not separated by an empty line. |
404 |
|
|
HeadComment string |
405 |
|
|
|
406 |
|
|
// LineComment holds any comments at the end of the line where the node is in. |
407 |
|
|
LineComment string |
408 |
|
|
|
409 |
|
|
// FootComment holds any comments following the node and before empty lines. |
410 |
|
|
FootComment string |
411 |
|
|
|
412 |
|
|
// Line and Column hold the node position in the decoded YAML text. |
413 |
|
|
// These fields are not respected when encoding the node. |
414 |
|
|
Line int |
415 |
|
|
Column int |
416 |
|
|
} |
417 |
|
|
|
418 |
|
|
// IsZero returns whether the node has all of its fields unset. |
419 |
|
|
func (n *Node) IsZero() bool { |
420 |
|
|
return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil && |
421 |
|
|
n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0 |
422 |
|
|
} |
423 |
|
|
|
424 |
|
|
|
425 |
|
|
// LongTag returns the long form of the tag that indicates the data type for |
426 |
|
|
// the node. If the Tag field isn't explicitly defined, one will be computed |
427 |
|
|
// based on the node properties. |
428 |
|
|
func (n *Node) LongTag() string { |
429 |
|
|
return longTag(n.ShortTag()) |
430 |
|
|
} |
431 |
|
|
|
432 |
|
|
// ShortTag returns the short form of the YAML tag that indicates data type for |
433 |
|
|
// the node. If the Tag field isn't explicitly defined, one will be computed |
434 |
|
|
// based on the node properties. |
435 |
|
|
func (n *Node) ShortTag() string { |
436 |
|
|
if n.indicatedString() { |
437 |
|
|
return strTag |
438 |
|
|
} |
439 |
|
|
if n.Tag == "" || n.Tag == "!" { |
440 |
|
|
switch n.Kind { |
441 |
|
|
case MappingNode: |
442 |
|
|
return mapTag |
443 |
|
|
case SequenceNode: |
444 |
|
|
return seqTag |
445 |
|
|
case AliasNode: |
446 |
|
|
if n.Alias != nil { |
447 |
|
|
return n.Alias.ShortTag() |
448 |
|
|
} |
449 |
|
|
case ScalarNode: |
450 |
|
|
tag, _ := resolve("", n.Value) |
451 |
|
|
return tag |
452 |
|
|
case 0: |
453 |
|
|
// Special case to make the zero value convenient. |
454 |
|
|
if n.IsZero() { |
455 |
|
|
return nullTag |
456 |
|
|
} |
457 |
|
|
} |
458 |
|
|
return "" |
459 |
|
|
} |
460 |
|
|
return shortTag(n.Tag) |
461 |
|
|
} |
462 |
|
|
|
463 |
|
|
func (n *Node) indicatedString() bool { |
464 |
|
|
return n.Kind == ScalarNode && |
465 |
|
|
(shortTag(n.Tag) == strTag || |
466 |
|
|
(n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0) |
467 |
|
|
} |
468 |
|
|
|
469 |
|
|
// SetString is a convenience function that sets the node to a string value |
470 |
|
|
// and defines its style in a pleasant way depending on its content. |
471 |
|
|
func (n *Node) SetString(s string) { |
472 |
|
|
n.Kind = ScalarNode |
473 |
|
|
if utf8.ValidString(s) { |
474 |
|
|
n.Value = s |
475 |
|
|
n.Tag = strTag |
476 |
|
|
} else { |
477 |
|
|
n.Value = encodeBase64(s) |
478 |
|
|
n.Tag = binaryTag |
479 |
|
|
} |
480 |
|
|
if strings.Contains(n.Value, "\n") { |
481 |
|
|
n.Style = LiteralStyle |
482 |
|
|
} |
483 |
|
|
} |
484 |
|
|
|
485 |
|
|
// -------------------------------------------------------------------------- |
486 |
|
|
// Maintain a mapping of keys to structure field indexes |
487 |
|
|
|
488 |
|
|
// The code in this section was copied from mgo/bson. |
489 |
|
|
|
490 |
|
|
// structInfo holds details for the serialization of fields of |
491 |
|
|
// a given struct. |
492 |
|
|
type structInfo struct { |
493 |
|
|
FieldsMap map[string]fieldInfo |
494 |
|
|
FieldsList []fieldInfo |
495 |
|
|
|
496 |
|
|
// InlineMap is the number of the field in the struct that |
497 |
|
|
// contains an ,inline map, or -1 if there's none. |
498 |
|
|
InlineMap int |
499 |
|
|
|
500 |
|
|
// InlineUnmarshalers holds indexes to inlined fields that |
501 |
|
|
// contain unmarshaler values. |
502 |
|
|
InlineUnmarshalers [][]int |
503 |
|
|
} |
504 |
|
|
|
505 |
|
|
type fieldInfo struct { |
506 |
|
|
Key string |
507 |
|
|
Num int |
508 |
|
|
OmitEmpty bool |
509 |
|
|
Flow bool |
510 |
|
|
// Id holds the unique field identifier, so we can cheaply |
511 |
|
|
// check for field duplicates without maintaining an extra map. |
512 |
|
|
Id int |
513 |
|
|
|
514 |
|
|
// Inline holds the field index if the field is part of an inlined struct. |
515 |
|
|
Inline []int |
516 |
|
|
} |
517 |
|
|
|
518 |
|
|
var structMap = make(map[reflect.Type]*structInfo) |
519 |
|
|
var fieldMapMutex sync.RWMutex |
520 |
|
|
var unmarshalerType reflect.Type |
521 |
|
|
|
522 |
|
|
func init() { |
523 |
|
|
var v Unmarshaler |
524 |
|
|
unmarshalerType = reflect.ValueOf(&v).Elem().Type() |
525 |
|
|
} |
526 |
|
|
|
527 |
|
|
func getStructInfo(st reflect.Type) (*structInfo, error) { |
528 |
|
|
fieldMapMutex.RLock() |
529 |
|
|
sinfo, found := structMap[st] |
530 |
|
|
fieldMapMutex.RUnlock() |
531 |
|
|
if found { |
532 |
|
|
return sinfo, nil |
533 |
|
|
} |
534 |
|
|
|
535 |
|
|
n := st.NumField() |
536 |
|
|
fieldsMap := make(map[string]fieldInfo) |
537 |
|
|
fieldsList := make([]fieldInfo, 0, n) |
538 |
|
|
inlineMap := -1 |
539 |
|
|
inlineUnmarshalers := [][]int(nil) |
540 |
|
|
for i := 0; i != n; i++ { |
541 |
|
|
field := st.Field(i) |
542 |
|
|
if field.PkgPath != "" && !field.Anonymous { |
543 |
|
|
continue // Private field |
544 |
|
|
} |
545 |
|
|
|
546 |
|
|
info := fieldInfo{Num: i} |
547 |
|
|
|
548 |
|
|
tag := field.Tag.Get("yaml") |
549 |
|
|
if tag == "" && strings.Index(string(field.Tag), ":") < 0 { |
550 |
|
|
tag = string(field.Tag) |
551 |
|
|
} |
552 |
|
|
if tag == "-" { |
553 |
|
|
continue |
554 |
|
|
} |
555 |
|
|
|
556 |
|
|
inline := false |
557 |
|
|
fields := strings.Split(tag, ",") |
558 |
|
|
if len(fields) > 1 { |
559 |
|
|
for _, flag := range fields[1:] { |
560 |
|
|
switch flag { |
561 |
|
|
case "omitempty": |
562 |
|
|
info.OmitEmpty = true |
563 |
|
|
case "flow": |
564 |
|
|
info.Flow = true |
565 |
|
|
case "inline": |
566 |
|
|
inline = true |
567 |
|
|
default: |
568 |
|
|
return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st)) |
569 |
|
|
} |
570 |
|
|
} |
571 |
|
|
tag = fields[0] |
572 |
|
|
} |
573 |
|
|
|
574 |
|
|
if inline { |
575 |
|
|
switch field.Type.Kind() { |
576 |
|
|
case reflect.Map: |
577 |
|
|
if inlineMap >= 0 { |
578 |
|
|
return nil, errors.New("multiple ,inline maps in struct " + st.String()) |
579 |
|
|
} |
580 |
|
|
if field.Type.Key() != reflect.TypeOf("") { |
581 |
|
|
return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String()) |
582 |
|
|
} |
583 |
|
|
inlineMap = info.Num |
584 |
|
|
case reflect.Struct, reflect.Ptr: |
585 |
|
|
ftype := field.Type |
586 |
|
|
for ftype.Kind() == reflect.Ptr { |
587 |
|
|
ftype = ftype.Elem() |
588 |
|
|
} |
589 |
|
|
if ftype.Kind() != reflect.Struct { |
590 |
|
|
return nil, errors.New("option ,inline may only be used on a struct or map field") |
591 |
|
|
} |
592 |
|
|
if reflect.PtrTo(ftype).Implements(unmarshalerType) { |
593 |
|
|
inlineUnmarshalers = append(inlineUnmarshalers, []int{i}) |
594 |
|
|
} else { |
595 |
|
|
sinfo, err := getStructInfo(ftype) |
596 |
|
|
if err != nil { |
597 |
|
|
return nil, err |
598 |
|
|
} |
599 |
|
|
for _, index := range sinfo.InlineUnmarshalers { |
600 |
|
|
inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...)) |
601 |
|
|
} |
602 |
|
|
for _, finfo := range sinfo.FieldsList { |
603 |
|
|
if _, found := fieldsMap[finfo.Key]; found { |
604 |
|
|
msg := "duplicated key '" + finfo.Key + "' in struct " + st.String() |
605 |
|
|
return nil, errors.New(msg) |
606 |
|
|
} |
607 |
|
|
if finfo.Inline == nil { |
608 |
|
|
finfo.Inline = []int{i, finfo.Num} |
609 |
|
|
} else { |
610 |
|
|
finfo.Inline = append([]int{i}, finfo.Inline...) |
611 |
|
|
} |
612 |
|
|
finfo.Id = len(fieldsList) |
613 |
|
|
fieldsMap[finfo.Key] = finfo |
614 |
|
|
fieldsList = append(fieldsList, finfo) |
615 |
|
|
} |
616 |
|
|
} |
617 |
|
|
default: |
618 |
|
|
return nil, errors.New("option ,inline may only be used on a struct or map field") |
619 |
|
|
} |
620 |
|
|
continue |
621 |
|
|
} |
622 |
|
|
|
623 |
|
|
if tag != "" { |
624 |
|
|
info.Key = tag |
625 |
|
|
} else { |
626 |
|
|
info.Key = strings.ToLower(field.Name) |
627 |
|
|
} |
628 |
|
|
|
629 |
|
|
if _, found = fieldsMap[info.Key]; found { |
630 |
|
|
msg := "duplicated key '" + info.Key + "' in struct " + st.String() |
631 |
|
|
return nil, errors.New(msg) |
632 |
|
|
} |
633 |
|
|
|
634 |
|
|
info.Id = len(fieldsList) |
635 |
|
|
fieldsList = append(fieldsList, info) |
636 |
|
|
fieldsMap[info.Key] = info |
637 |
|
|
} |
638 |
|
|
|
639 |
|
|
sinfo = &structInfo{ |
640 |
|
|
FieldsMap: fieldsMap, |
641 |
|
|
FieldsList: fieldsList, |
642 |
|
|
InlineMap: inlineMap, |
643 |
|
|
InlineUnmarshalers: inlineUnmarshalers, |
644 |
|
|
} |
645 |
|
|
|
646 |
|
|
fieldMapMutex.Lock() |
647 |
|
|
structMap[st] = sinfo |
648 |
|
|
fieldMapMutex.Unlock() |
649 |
|
|
return sinfo, nil |
650 |
|
|
} |
651 |
|
|
|
652 |
|
|
// IsZeroer is used to check whether an object is zero to |
653 |
|
|
// determine whether it should be omitted when marshaling |
654 |
|
|
// with the omitempty flag. One notable implementation |
655 |
|
|
// is time.Time. |
656 |
|
|
type IsZeroer interface { |
657 |
|
|
IsZero() bool |
658 |
|
|
} |
659 |
|
|
|
660 |
|
|
func isZero(v reflect.Value) bool { |
661 |
|
|
kind := v.Kind() |
662 |
|
|
if z, ok := v.Interface().(IsZeroer); ok { |
663 |
|
|
if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { |
664 |
|
|
return true |
665 |
|
|
} |
666 |
|
|
return z.IsZero() |
667 |
|
|
} |
668 |
|
|
switch kind { |
669 |
|
|
case reflect.String: |
670 |
|
|
return len(v.String()) == 0 |
671 |
|
|
case reflect.Interface, reflect.Ptr: |
672 |
|
|
return v.IsNil() |
673 |
|
|
case reflect.Slice: |
674 |
|
|
return v.Len() == 0 |
675 |
|
|
case reflect.Map: |
676 |
|
|
return v.Len() == 0 |
677 |
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
678 |
|
|
return v.Int() == 0 |
679 |
|
|
case reflect.Float32, reflect.Float64: |
680 |
|
|
return v.Float() == 0 |
681 |
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
682 |
|
|
return v.Uint() == 0 |
683 |
|
|
case reflect.Bool: |
684 |
|
|
return !v.Bool() |
685 |
|
|
case reflect.Struct: |
686 |
|
|
vt := v.Type() |
687 |
|
|
for i := v.NumField() - 1; i >= 0; i-- { |
688 |
|
|
if vt.Field(i).PkgPath != "" { |
689 |
|
|
continue // Private field |
690 |
|
|
} |
691 |
|
|
if !isZero(v.Field(i)) { |
692 |
|
|
return false |
693 |
|
|
} |
694 |
|
|
} |
695 |
|
|
return true |
696 |
|
|
} |
697 |
|
|
return false |
698 |
|
|
} |