yaml.go 11 KB

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