1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package swag
16
17 import (
18 "encoding/json"
19 "errors"
20 "fmt"
21 "path/filepath"
22 "reflect"
23 "sort"
24 "strconv"
25
26 "github.com/mailru/easyjson/jlexer"
27 "github.com/mailru/easyjson/jwriter"
28 yaml "gopkg.in/yaml.v3"
29 )
30
31
32 func YAMLMatcher(path string) bool {
33 ext := filepath.Ext(path)
34 return ext == ".yaml" || ext == ".yml"
35 }
36
37
38 func YAMLToJSON(data interface{}) (json.RawMessage, error) {
39 jm, err := transformData(data)
40 if err != nil {
41 return nil, err
42 }
43 b, err := WriteJSON(jm)
44 return json.RawMessage(b), err
45 }
46
47
48 func BytesToYAMLDoc(data []byte) (interface{}, error) {
49 var document yaml.Node
50 if err := yaml.Unmarshal(data, &document); err != nil {
51 return nil, err
52 }
53 if document.Kind != yaml.DocumentNode || len(document.Content) != 1 || document.Content[0].Kind != yaml.MappingNode {
54 return nil, errors.New("only YAML documents that are objects are supported")
55 }
56 return &document, nil
57 }
58
59 func yamlNode(root *yaml.Node) (interface{}, error) {
60 switch root.Kind {
61 case yaml.DocumentNode:
62 return yamlDocument(root)
63 case yaml.SequenceNode:
64 return yamlSequence(root)
65 case yaml.MappingNode:
66 return yamlMapping(root)
67 case yaml.ScalarNode:
68 return yamlScalar(root)
69 case yaml.AliasNode:
70 return yamlNode(root.Alias)
71 default:
72 return nil, fmt.Errorf("unsupported YAML node type: %v", root.Kind)
73 }
74 }
75
76 func yamlDocument(node *yaml.Node) (interface{}, error) {
77 if len(node.Content) != 1 {
78 return nil, fmt.Errorf("unexpected YAML Document node content length: %d", len(node.Content))
79 }
80 return yamlNode(node.Content[0])
81 }
82
83 func yamlMapping(node *yaml.Node) (interface{}, error) {
84 m := make(JSONMapSlice, len(node.Content)/2)
85
86 var j int
87 for i := 0; i < len(node.Content); i += 2 {
88 var nmi JSONMapItem
89 k, err := yamlStringScalarC(node.Content[i])
90 if err != nil {
91 return nil, fmt.Errorf("unable to decode YAML map key: %w", err)
92 }
93 nmi.Key = k
94 v, err := yamlNode(node.Content[i+1])
95 if err != nil {
96 return nil, fmt.Errorf("unable to process YAML map value for key %q: %w", k, err)
97 }
98 nmi.Value = v
99 m[j] = nmi
100 j++
101 }
102 return m, nil
103 }
104
105 func yamlSequence(node *yaml.Node) (interface{}, error) {
106 s := make([]interface{}, 0)
107
108 for i := 0; i < len(node.Content); i++ {
109
110 v, err := yamlNode(node.Content[i])
111 if err != nil {
112 return nil, fmt.Errorf("unable to decode YAML sequence value: %w", err)
113 }
114 s = append(s, v)
115 }
116 return s, nil
117 }
118
119 const (
120 yamlStringScalar = "tag:yaml.org,2002:str"
121 yamlIntScalar = "tag:yaml.org,2002:int"
122 yamlBoolScalar = "tag:yaml.org,2002:bool"
123 yamlFloatScalar = "tag:yaml.org,2002:float"
124 yamlTimestamp = "tag:yaml.org,2002:timestamp"
125 yamlNull = "tag:yaml.org,2002:null"
126 )
127
128 func yamlScalar(node *yaml.Node) (interface{}, error) {
129 switch node.LongTag() {
130 case yamlStringScalar:
131 return node.Value, nil
132 case yamlBoolScalar:
133 b, err := strconv.ParseBool(node.Value)
134 if err != nil {
135 return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting bool content: %w", node.Value, err)
136 }
137 return b, nil
138 case yamlIntScalar:
139 i, err := strconv.ParseInt(node.Value, 10, 64)
140 if err != nil {
141 return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting integer content: %w", node.Value, err)
142 }
143 return i, nil
144 case yamlFloatScalar:
145 f, err := strconv.ParseFloat(node.Value, 64)
146 if err != nil {
147 return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting float content: %w", node.Value, err)
148 }
149 return f, nil
150 case yamlTimestamp:
151 return node.Value, nil
152 case yamlNull:
153 return nil, nil
154 default:
155 return nil, fmt.Errorf("YAML tag %q is not supported", node.LongTag())
156 }
157 }
158
159 func yamlStringScalarC(node *yaml.Node) (string, error) {
160 if node.Kind != yaml.ScalarNode {
161 return "", fmt.Errorf("expecting a string scalar but got %q", node.Kind)
162 }
163 switch node.LongTag() {
164 case yamlStringScalar, yamlIntScalar, yamlFloatScalar:
165 return node.Value, nil
166 default:
167 return "", fmt.Errorf("YAML tag %q is not supported as map key", node.LongTag())
168 }
169 }
170
171
172 type JSONMapSlice []JSONMapItem
173
174
175 func (s JSONMapSlice) MarshalJSON() ([]byte, error) {
176 w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty}
177 s.MarshalEasyJSON(w)
178 return w.BuildBytes()
179 }
180
181
182 func (s JSONMapSlice) MarshalEasyJSON(w *jwriter.Writer) {
183 w.RawByte('{')
184
185 ln := len(s)
186 last := ln - 1
187 for i := 0; i < ln; i++ {
188 s[i].MarshalEasyJSON(w)
189 if i != last {
190 w.RawByte(',')
191 }
192 }
193
194 w.RawByte('}')
195 }
196
197
198 func (s *JSONMapSlice) UnmarshalJSON(data []byte) error {
199 l := jlexer.Lexer{Data: data}
200 s.UnmarshalEasyJSON(&l)
201 return l.Error()
202 }
203
204
205 func (s *JSONMapSlice) UnmarshalEasyJSON(in *jlexer.Lexer) {
206 if in.IsNull() {
207 in.Skip()
208 return
209 }
210
211 var result JSONMapSlice
212 in.Delim('{')
213 for !in.IsDelim('}') {
214 var mi JSONMapItem
215 mi.UnmarshalEasyJSON(in)
216 result = append(result, mi)
217 }
218 *s = result
219 }
220
221 func (s JSONMapSlice) MarshalYAML() (interface{}, error) {
222 var n yaml.Node
223 n.Kind = yaml.DocumentNode
224 var nodes []*yaml.Node
225 for _, item := range s {
226 nn, err := json2yaml(item.Value)
227 if err != nil {
228 return nil, err
229 }
230 ns := []*yaml.Node{
231 {
232 Kind: yaml.ScalarNode,
233 Tag: yamlStringScalar,
234 Value: item.Key,
235 },
236 nn,
237 }
238 nodes = append(nodes, ns...)
239 }
240
241 n.Content = []*yaml.Node{
242 {
243 Kind: yaml.MappingNode,
244 Content: nodes,
245 },
246 }
247
248 return yaml.Marshal(&n)
249 }
250
251 func isNil(input interface{}) bool {
252 if input == nil {
253 return true
254 }
255 kind := reflect.TypeOf(input).Kind()
256 switch kind {
257 case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
258 return reflect.ValueOf(input).IsNil()
259 default:
260 return false
261 }
262 }
263
264 func json2yaml(item interface{}) (*yaml.Node, error) {
265 if isNil(item) {
266 return &yaml.Node{
267 Kind: yaml.ScalarNode,
268 Value: "null",
269 }, nil
270 }
271
272 switch val := item.(type) {
273 case JSONMapSlice:
274 var n yaml.Node
275 n.Kind = yaml.MappingNode
276 for i := range val {
277 childNode, err := json2yaml(&val[i].Value)
278 if err != nil {
279 return nil, err
280 }
281 n.Content = append(n.Content, &yaml.Node{
282 Kind: yaml.ScalarNode,
283 Tag: yamlStringScalar,
284 Value: val[i].Key,
285 }, childNode)
286 }
287 return &n, nil
288 case map[string]interface{}:
289 var n yaml.Node
290 n.Kind = yaml.MappingNode
291 keys := make([]string, 0, len(val))
292 for k := range val {
293 keys = append(keys, k)
294 }
295 sort.Strings(keys)
296
297 for _, k := range keys {
298 v := val[k]
299 childNode, err := json2yaml(v)
300 if err != nil {
301 return nil, err
302 }
303 n.Content = append(n.Content, &yaml.Node{
304 Kind: yaml.ScalarNode,
305 Tag: yamlStringScalar,
306 Value: k,
307 }, childNode)
308 }
309 return &n, nil
310 case []interface{}:
311 var n yaml.Node
312 n.Kind = yaml.SequenceNode
313 for i := range val {
314 childNode, err := json2yaml(val[i])
315 if err != nil {
316 return nil, err
317 }
318 n.Content = append(n.Content, childNode)
319 }
320 return &n, nil
321 case string:
322 return &yaml.Node{
323 Kind: yaml.ScalarNode,
324 Tag: yamlStringScalar,
325 Value: val,
326 }, nil
327 case float64:
328 return &yaml.Node{
329 Kind: yaml.ScalarNode,
330 Tag: yamlFloatScalar,
331 Value: strconv.FormatFloat(val, 'f', -1, 64),
332 }, nil
333 case int64:
334 return &yaml.Node{
335 Kind: yaml.ScalarNode,
336 Tag: yamlIntScalar,
337 Value: strconv.FormatInt(val, 10),
338 }, nil
339 case uint64:
340 return &yaml.Node{
341 Kind: yaml.ScalarNode,
342 Tag: yamlIntScalar,
343 Value: strconv.FormatUint(val, 10),
344 }, nil
345 case bool:
346 return &yaml.Node{
347 Kind: yaml.ScalarNode,
348 Tag: yamlBoolScalar,
349 Value: strconv.FormatBool(val),
350 }, nil
351 default:
352 return nil, fmt.Errorf("unhandled type: %T", val)
353 }
354 }
355
356
357 type JSONMapItem struct {
358 Key string
359 Value interface{}
360 }
361
362
363 func (s JSONMapItem) MarshalJSON() ([]byte, error) {
364 w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty}
365 s.MarshalEasyJSON(w)
366 return w.BuildBytes()
367 }
368
369
370 func (s JSONMapItem) MarshalEasyJSON(w *jwriter.Writer) {
371 w.String(s.Key)
372 w.RawByte(':')
373 w.Raw(WriteJSON(s.Value))
374 }
375
376
377 func (s *JSONMapItem) UnmarshalJSON(data []byte) error {
378 l := jlexer.Lexer{Data: data}
379 s.UnmarshalEasyJSON(&l)
380 return l.Error()
381 }
382
383
384 func (s *JSONMapItem) UnmarshalEasyJSON(in *jlexer.Lexer) {
385 key := in.UnsafeString()
386 in.WantColon()
387 value := in.Interface()
388 in.WantComma()
389 s.Key = key
390 s.Value = value
391 }
392
393 func transformData(input interface{}) (out interface{}, err error) {
394 format := func(t interface{}) (string, error) {
395 switch k := t.(type) {
396 case string:
397 return k, nil
398 case uint:
399 return strconv.FormatUint(uint64(k), 10), nil
400 case uint8:
401 return strconv.FormatUint(uint64(k), 10), nil
402 case uint16:
403 return strconv.FormatUint(uint64(k), 10), nil
404 case uint32:
405 return strconv.FormatUint(uint64(k), 10), nil
406 case uint64:
407 return strconv.FormatUint(k, 10), nil
408 case int:
409 return strconv.Itoa(k), nil
410 case int8:
411 return strconv.FormatInt(int64(k), 10), nil
412 case int16:
413 return strconv.FormatInt(int64(k), 10), nil
414 case int32:
415 return strconv.FormatInt(int64(k), 10), nil
416 case int64:
417 return strconv.FormatInt(k, 10), nil
418 default:
419 return "", fmt.Errorf("unexpected map key type, got: %T", k)
420 }
421 }
422
423 switch in := input.(type) {
424 case yaml.Node:
425 return yamlNode(&in)
426 case *yaml.Node:
427 return yamlNode(in)
428 case map[interface{}]interface{}:
429 o := make(JSONMapSlice, 0, len(in))
430 for ke, va := range in {
431 var nmi JSONMapItem
432 if nmi.Key, err = format(ke); err != nil {
433 return nil, err
434 }
435
436 v, ert := transformData(va)
437 if ert != nil {
438 return nil, ert
439 }
440 nmi.Value = v
441 o = append(o, nmi)
442 }
443 return o, nil
444 case []interface{}:
445 len1 := len(in)
446 o := make([]interface{}, len1)
447 for i := 0; i < len1; i++ {
448 o[i], err = transformData(in[i])
449 if err != nil {
450 return nil, err
451 }
452 }
453 return o, nil
454 }
455 return input, nil
456 }
457
458
459 func YAMLDoc(path string) (json.RawMessage, error) {
460 yamlDoc, err := YAMLData(path)
461 if err != nil {
462 return nil, err
463 }
464
465 data, err := YAMLToJSON(yamlDoc)
466 if err != nil {
467 return nil, err
468 }
469
470 return data, nil
471 }
472
473
474 func YAMLData(path string) (interface{}, error) {
475 data, err := LoadFromFileOrHTTP(path)
476 if err != nil {
477 return nil, err
478 }
479
480 return BytesToYAMLDoc(data)
481 }
482
View as plain text