blob: f458c81a84a866bb866fadca0b14593342ebedf4 [file] [log] [blame]
Serge Bazanskicc25bdf2018-10-25 14:02:58 +02001// 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
59// JSONMapSlice represent a JSON object, with the order of keys maintained
60type JSONMapSlice []JSONMapItem
61
62// MarshalJSON renders a JSONMapSlice as JSON
63func (s JSONMapSlice) MarshalJSON() ([]byte, error) {
64 w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty}
65 s.MarshalEasyJSON(w)
66 return w.BuildBytes()
67}
68
69// MarshalEasyJSON renders a JSONMapSlice as JSON, using easyJSON
70func (s JSONMapSlice) MarshalEasyJSON(w *jwriter.Writer) {
71 w.RawByte('{')
72
73 ln := len(s)
74 last := ln - 1
75 for i := 0; i < ln; i++ {
76 s[i].MarshalEasyJSON(w)
77 if i != last { // last item
78 w.RawByte(',')
79 }
80 }
81
82 w.RawByte('}')
83}
84
85// UnmarshalJSON makes a JSONMapSlice from JSON
86func (s *JSONMapSlice) UnmarshalJSON(data []byte) error {
87 l := jlexer.Lexer{Data: data}
88 s.UnmarshalEasyJSON(&l)
89 return l.Error()
90}
91
92// UnmarshalEasyJSON makes a JSONMapSlice from JSON, using easyJSON
93func (s *JSONMapSlice) UnmarshalEasyJSON(in *jlexer.Lexer) {
94 if in.IsNull() {
95 in.Skip()
96 return
97 }
98
99 var result JSONMapSlice
100 in.Delim('{')
101 for !in.IsDelim('}') {
102 var mi JSONMapItem
103 mi.UnmarshalEasyJSON(in)
104 result = append(result, mi)
105 }
106 *s = result
107}
108
109// JSONMapItem represents the value of a key in a JSON object held by JSONMapSlice
110type JSONMapItem struct {
111 Key string
112 Value interface{}
113}
114
115// MarshalJSON renders a JSONMapItem as JSON
116func (s JSONMapItem) MarshalJSON() ([]byte, error) {
117 w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty}
118 s.MarshalEasyJSON(w)
119 return w.BuildBytes()
120}
121
122// MarshalEasyJSON renders a JSONMapItem as JSON, using easyJSON
123func (s JSONMapItem) MarshalEasyJSON(w *jwriter.Writer) {
124 w.String(s.Key)
125 w.RawByte(':')
126 w.Raw(WriteJSON(s.Value))
127}
128
129// UnmarshalJSON makes a JSONMapItem from JSON
130func (s *JSONMapItem) UnmarshalJSON(data []byte) error {
131 l := jlexer.Lexer{Data: data}
132 s.UnmarshalEasyJSON(&l)
133 return l.Error()
134}
135
136// UnmarshalEasyJSON makes a JSONMapItem from JSON, using easyJSON
137func (s *JSONMapItem) UnmarshalEasyJSON(in *jlexer.Lexer) {
138 key := in.UnsafeString()
139 in.WantColon()
140 value := in.Interface()
141 in.WantComma()
142 s.Key = key
143 s.Value = value
144}
145
146func transformData(input interface{}) (out interface{}, err error) {
147 switch in := input.(type) {
148 case yaml.MapSlice:
149
150 o := make(JSONMapSlice, len(in))
151 for i, mi := range in {
152 var nmi JSONMapItem
153 switch k := mi.Key.(type) {
154 case string:
155 nmi.Key = k
156 case int:
157 nmi.Key = strconv.Itoa(k)
158 default:
159 return nil, fmt.Errorf("types don't match expect map key string or int got: %T", mi.Key)
160 }
161
162 v, ert := transformData(mi.Value)
163 if ert != nil {
164 return nil, ert
165 }
166 nmi.Value = v
167 o[i] = nmi
168 }
169 return o, nil
170 case map[interface{}]interface{}:
171 o := make(JSONMapSlice, 0, len(in))
172 for ke, va := range in {
173 var nmi JSONMapItem
174 switch k := ke.(type) {
175 case string:
176 nmi.Key = k
177 case int:
178 nmi.Key = strconv.Itoa(k)
179 default:
180 return nil, fmt.Errorf("types don't match expect map key string or int got: %T", ke)
181 }
182
183 v, ert := transformData(va)
184 if ert != nil {
185 return nil, ert
186 }
187 nmi.Value = v
188 o = append(o, nmi)
189 }
190 return o, nil
191 case []interface{}:
192 len1 := len(in)
193 o := make([]interface{}, len1)
194 for i := 0; i < len1; i++ {
195 o[i], err = transformData(in[i])
196 if err != nil {
197 return nil, err
198 }
199 }
200 return o, nil
201 }
202 return input, nil
203}
204
205// YAMLDoc loads a yaml document from either http or a file and converts it to json
206func YAMLDoc(path string) (json.RawMessage, error) {
207 yamlDoc, err := YAMLData(path)
208 if err != nil {
209 return nil, err
210 }
211
212 data, err := YAMLToJSON(yamlDoc)
213 if err != nil {
214 return nil, err
215 }
216
217 return data, nil
218}
219
220// YAMLData loads a yaml document from either http or a file
221func YAMLData(path string) (interface{}, error) {
222 data, err := LoadFromFileOrHTTP(path)
223 if err != nil {
224 return nil, err
225 }
226
227 return BytesToYAMLDoc(data)
228}