1// 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 yaml "gopkg.in/yaml.v2" 26) 27 28// YAMLMatcher matches yaml 29func YAMLMatcher(path string) bool { 30 ext := filepath.Ext(path) 31 return ext == ".yaml" || ext == ".yml" 32} 33 34// YAMLToJSON converts YAML unmarshaled data into json compatible data 35func YAMLToJSON(data interface{}) (json.RawMessage, error) { 36 jm, err := transformData(data) 37 if err != nil { 38 return nil, err 39 } 40 b, err := WriteJSON(jm) 41 return json.RawMessage(b), err 42} 43 44// BytesToYAMLDoc converts a byte slice into a YAML document 45func BytesToYAMLDoc(data []byte) (interface{}, error) { 46 var canary map[interface{}]interface{} // validate this is an object and not a different type 47 if err := yaml.Unmarshal(data, &canary); err != nil { 48 return nil, err 49 } 50 51 var document yaml.MapSlice // preserve order that is present in the document 52 if err := yaml.Unmarshal(data, &document); err != nil { 53 return nil, err 54 } 55 return document, nil 56} 57 58// JSONMapSlice represent a JSON object, with the order of keys maintained 59type JSONMapSlice []JSONMapItem 60 61// MarshalJSON renders a JSONMapSlice as JSON 62func (s JSONMapSlice) MarshalJSON() ([]byte, error) { 63 w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty} 64 s.MarshalEasyJSON(w) 65 return w.BuildBytes() 66} 67 68// MarshalEasyJSON renders a JSONMapSlice as JSON, using easyJSON 69func (s JSONMapSlice) MarshalEasyJSON(w *jwriter.Writer) { 70 w.RawByte('{') 71 72 ln := len(s) 73 last := ln - 1 74 for i := 0; i < ln; i++ { 75 s[i].MarshalEasyJSON(w) 76 if i != last { // last item 77 w.RawByte(',') 78 } 79 } 80 81 w.RawByte('}') 82} 83 84// UnmarshalJSON makes a JSONMapSlice from JSON 85func (s *JSONMapSlice) UnmarshalJSON(data []byte) error { 86 l := jlexer.Lexer{Data: data} 87 s.UnmarshalEasyJSON(&l) 88 return l.Error() 89} 90 91// UnmarshalEasyJSON makes a JSONMapSlice from JSON, using easyJSON 92func (s *JSONMapSlice) UnmarshalEasyJSON(in *jlexer.Lexer) { 93 if in.IsNull() { 94 in.Skip() 95 return 96 } 97 98 var result JSONMapSlice 99 in.Delim('{') 100 for !in.IsDelim('}') { 101 var mi JSONMapItem 102 mi.UnmarshalEasyJSON(in) 103 result = append(result, mi) 104 } 105 *s = result 106} 107 108// JSONMapItem represents the value of a key in a JSON object held by JSONMapSlice 109type JSONMapItem struct { 110 Key string 111 Value interface{} 112} 113 114// MarshalJSON renders a JSONMapItem as JSON 115func (s JSONMapItem) MarshalJSON() ([]byte, error) { 116 w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty} 117 s.MarshalEasyJSON(w) 118 return w.BuildBytes() 119} 120 121// MarshalEasyJSON renders a JSONMapItem as JSON, using easyJSON 122func (s JSONMapItem) MarshalEasyJSON(w *jwriter.Writer) { 123 w.String(s.Key) 124 w.RawByte(':') 125 w.Raw(WriteJSON(s.Value)) 126} 127 128// UnmarshalJSON makes a JSONMapItem from JSON 129func (s *JSONMapItem) UnmarshalJSON(data []byte) error { 130 l := jlexer.Lexer{Data: data} 131 s.UnmarshalEasyJSON(&l) 132 return l.Error() 133} 134 135// UnmarshalEasyJSON makes a JSONMapItem from JSON, using easyJSON 136func (s *JSONMapItem) UnmarshalEasyJSON(in *jlexer.Lexer) { 137 key := in.UnsafeString() 138 in.WantColon() 139 value := in.Interface() 140 in.WantComma() 141 s.Key = key 142 s.Value = value 143} 144 145func transformData(input interface{}) (out interface{}, err error) { 146 format := func(t interface{}) (string, error) { 147 switch k := t.(type) { 148 case string: 149 return k, nil 150 case uint: 151 return strconv.FormatUint(uint64(k), 10), nil 152 case uint8: 153 return strconv.FormatUint(uint64(k), 10), nil 154 case uint16: 155 return strconv.FormatUint(uint64(k), 10), nil 156 case uint32: 157 return strconv.FormatUint(uint64(k), 10), nil 158 case uint64: 159 return strconv.FormatUint(k, 10), nil 160 case int: 161 return strconv.Itoa(k), nil 162 case int8: 163 return strconv.FormatInt(int64(k), 10), nil 164 case int16: 165 return strconv.FormatInt(int64(k), 10), nil 166 case int32: 167 return strconv.FormatInt(int64(k), 10), nil 168 case int64: 169 return strconv.FormatInt(k, 10), nil 170 default: 171 return "", fmt.Errorf("unexpected map key type, got: %T", k) 172 } 173 } 174 175 switch in := input.(type) { 176 case yaml.MapSlice: 177 178 o := make(JSONMapSlice, len(in)) 179 for i, mi := range in { 180 var nmi JSONMapItem 181 if nmi.Key, err = format(mi.Key); err != nil { 182 return nil, err 183 } 184 185 v, ert := transformData(mi.Value) 186 if ert != nil { 187 return nil, ert 188 } 189 nmi.Value = v 190 o[i] = nmi 191 } 192 return o, nil 193 case map[interface{}]interface{}: 194 o := make(JSONMapSlice, 0, len(in)) 195 for ke, va := range in { 196 var nmi JSONMapItem 197 if nmi.Key, err = format(ke); err != nil { 198 return nil, err 199 } 200 201 v, ert := transformData(va) 202 if ert != nil { 203 return nil, ert 204 } 205 nmi.Value = v 206 o = append(o, nmi) 207 } 208 return o, nil 209 case []interface{}: 210 len1 := len(in) 211 o := make([]interface{}, len1) 212 for i := 0; i < len1; i++ { 213 o[i], err = transformData(in[i]) 214 if err != nil { 215 return nil, err 216 } 217 } 218 return o, nil 219 } 220 return input, nil 221} 222 223// YAMLDoc loads a yaml document from either http or a file and converts it to json 224func YAMLDoc(path string) (json.RawMessage, error) { 225 yamlDoc, err := YAMLData(path) 226 if err != nil { 227 return nil, err 228 } 229 230 data, err := YAMLToJSON(yamlDoc) 231 if err != nil { 232 return nil, err 233 } 234 235 return data, nil 236} 237 238// YAMLData loads a yaml document from either http or a file 239func YAMLData(path string) (interface{}, error) { 240 data, err := LoadFromFileOrHTTP(path) 241 if err != nil { 242 return nil, err 243 } 244 245 return BytesToYAMLDoc(data) 246} 247