blob: e2eff756832dc4c07cd3d5454cc4432312e1fa2c [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001// Copyright 2015 go-swagger maintainers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package swag
16
17import (
18 "encoding/json"
19 "fmt"
20 "path/filepath"
21 "strconv"
22
23 "github.com/mailru/easyjson/jlexer"
24 "github.com/mailru/easyjson/jwriter"
25
26 yaml "gopkg.in/yaml.v2"
27)
28
29// YAMLMatcher matches yaml
30func YAMLMatcher(path string) bool {
31 ext := filepath.Ext(path)
32 return ext == ".yaml" || ext == ".yml"
33}
34
35// YAMLToJSON converts YAML unmarshaled data into json compatible data
36func YAMLToJSON(data interface{}) (json.RawMessage, error) {
37 jm, err := transformData(data)
38 if err != nil {
39 return nil, err
40 }
41 b, err := WriteJSON(jm)
42 return json.RawMessage(b), err
43}
44
45// BytesToYAMLDoc converts a byte slice into a YAML document
46func BytesToYAMLDoc(data []byte) (interface{}, error) {
47 var canary map[interface{}]interface{} // validate this is an object and not a different type
48 if err := yaml.Unmarshal(data, &canary); err != nil {
49 return nil, err
50 }
51
52 var document yaml.MapSlice // preserve order that is present in the document
53 if err := yaml.Unmarshal(data, &document); err != nil {
54 return nil, err
55 }
56 return document, nil
57}
58
59type JSONMapSlice []JSONMapItem
60
61func (s JSONMapSlice) MarshalJSON() ([]byte, error) {
62 w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty}
63 s.MarshalEasyJSON(w)
64 return w.BuildBytes()
65}
66
67func (s JSONMapSlice) MarshalEasyJSON(w *jwriter.Writer) {
68 w.RawByte('{')
69
70 ln := len(s)
71 last := ln - 1
72 for i := 0; i < ln; i++ {
73 s[i].MarshalEasyJSON(w)
74 if i != last { // last item
75 w.RawByte(',')
76 }
77 }
78
79 w.RawByte('}')
80}
81
82func (s *JSONMapSlice) UnmarshalJSON(data []byte) error {
83 l := jlexer.Lexer{Data: data}
84 s.UnmarshalEasyJSON(&l)
85 return l.Error()
86}
87func (s *JSONMapSlice) UnmarshalEasyJSON(in *jlexer.Lexer) {
88 if in.IsNull() {
89 in.Skip()
90 return
91 }
92
93 var result JSONMapSlice
94 in.Delim('{')
95 for !in.IsDelim('}') {
96 var mi JSONMapItem
97 mi.UnmarshalEasyJSON(in)
98 result = append(result, mi)
99 }
100 *s = result
101}
102
103type JSONMapItem struct {
104 Key string
105 Value interface{}
106}
107
108func (s JSONMapItem) MarshalJSON() ([]byte, error) {
109 w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty}
110 s.MarshalEasyJSON(w)
111 return w.BuildBytes()
112}
113
114func (s JSONMapItem) MarshalEasyJSON(w *jwriter.Writer) {
115 w.String(s.Key)
116 w.RawByte(':')
117 w.Raw(WriteJSON(s.Value))
118}
119
120func (s *JSONMapItem) UnmarshalEasyJSON(in *jlexer.Lexer) {
121 key := in.UnsafeString()
122 in.WantColon()
123 value := in.Interface()
124 in.WantComma()
125 s.Key = key
126 s.Value = value
127}
128func (s *JSONMapItem) UnmarshalJSON(data []byte) error {
129 l := jlexer.Lexer{Data: data}
130 s.UnmarshalEasyJSON(&l)
131 return l.Error()
132}
133
134func transformData(input interface{}) (out interface{}, err error) {
135 switch in := input.(type) {
136 case yaml.MapSlice:
137
138 o := make(JSONMapSlice, len(in))
139 for i, mi := range in {
140 var nmi JSONMapItem
141 switch k := mi.Key.(type) {
142 case string:
143 nmi.Key = k
144 case int:
145 nmi.Key = strconv.Itoa(k)
146 default:
147 return nil, fmt.Errorf("types don't match expect map key string or int got: %T", mi.Key)
148 }
149
150 v, err := transformData(mi.Value)
151 if err != nil {
152 return nil, err
153 }
154 nmi.Value = v
155 o[i] = nmi
156 }
157 return o, nil
158 case map[interface{}]interface{}:
159 o := make(JSONMapSlice, 0, len(in))
160 for ke, va := range in {
161 var nmi JSONMapItem
162 switch k := ke.(type) {
163 case string:
164 nmi.Key = k
165 case int:
166 nmi.Key = strconv.Itoa(k)
167 default:
168 return nil, fmt.Errorf("types don't match expect map key string or int got: %T", ke)
169 }
170
171 v, err := transformData(va)
172 if err != nil {
173 return nil, err
174 }
175 nmi.Value = v
176 o = append(o, nmi)
177 }
178 return o, nil
179 case []interface{}:
180 len1 := len(in)
181 o := make([]interface{}, len1)
182 for i := 0; i < len1; i++ {
183 o[i], err = transformData(in[i])
184 if err != nil {
185 return nil, err
186 }
187 }
188 return o, nil
189 }
190 return input, nil
191}
192
193// YAMLDoc loads a yaml document from either http or a file and converts it to json
194func YAMLDoc(path string) (json.RawMessage, error) {
195 yamlDoc, err := YAMLData(path)
196 if err != nil {
197 return nil, err
198 }
199
200 data, err := YAMLToJSON(yamlDoc)
201 if err != nil {
202 return nil, err
203 }
204
205 return json.RawMessage(data), nil
206}
207
208// YAMLData loads a yaml document from either http or a file
209func YAMLData(path string) (interface{}, error) {
210 data, err := LoadFromFileOrHTTP(path)
211 if err != nil {
212 return nil, err
213 }
214
215 return BytesToYAMLDoc(data)
216}