...

Source file src/github.com/go-openapi/swag/yaml.go

Documentation: github.com/go-openapi/swag

     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  
    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  // YAMLMatcher matches yaml
    32  func YAMLMatcher(path string) bool {
    33  	ext := filepath.Ext(path)
    34  	return ext == ".yaml" || ext == ".yml"
    35  }
    36  
    37  // YAMLToJSON converts YAML unmarshaled data into json compatible data
    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  // BytesToYAMLDoc converts a byte slice into a YAML document
    48  func BytesToYAMLDoc(data []byte) (interface{}, error) {
    49  	var document yaml.Node // preserve order that is present in the document
    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 ( // See https://yaml.org/type/
   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 //nolint:nilnil
   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  // JSONMapSlice represent a JSON object, with the order of keys maintained
   172  type JSONMapSlice []JSONMapItem
   173  
   174  // MarshalJSON renders a JSONMapSlice as JSON
   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  // MarshalEasyJSON renders a JSONMapSlice as JSON, using easyJSON
   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 { // last item
   190  			w.RawByte(',')
   191  		}
   192  	}
   193  
   194  	w.RawByte('}')
   195  }
   196  
   197  // UnmarshalJSON makes a JSONMapSlice from JSON
   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  // UnmarshalEasyJSON makes a JSONMapSlice from JSON, using easyJSON
   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 { //nolint:exhaustive
   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  // JSONMapItem represents the value of a key in a JSON object held by JSONMapSlice
   357  type JSONMapItem struct {
   358  	Key   string
   359  	Value interface{}
   360  }
   361  
   362  // MarshalJSON renders a JSONMapItem as JSON
   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  // MarshalEasyJSON renders a JSONMapItem as JSON, using easyJSON
   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  // UnmarshalJSON makes a JSONMapItem from JSON
   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  // UnmarshalEasyJSON makes a JSONMapItem from JSON, using easyJSON
   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  // YAMLDoc loads a yaml document from either http or a file and converts it to json
   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  // YAMLData loads a yaml document from either http or a file
   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