github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/yaml/yaml.go (about)

     1  package yaml
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io"
     7  
     8  	"github.com/bingoohuang/gg/pkg/yaml/ast"
     9  	"github.com/bingoohuang/gg/pkg/yaml/internal/errors"
    10  	"golang.org/x/xerrors"
    11  )
    12  
    13  // BytesMarshaler interface may be implemented by types to customize their
    14  // behavior when being marshaled into a YAML document. The returned value
    15  // is marshaled in place of the original value implementing Marshaler.
    16  //
    17  // If an error is returned by MarshalYAML, the marshaling procedure stops
    18  // and returns with the provided error.
    19  type BytesMarshaler interface {
    20  	MarshalYAML() ([]byte, error)
    21  }
    22  
    23  // BytesMarshalerContext interface use BytesMarshaler with context.Context.
    24  type BytesMarshalerContext interface {
    25  	MarshalYAML(context.Context) ([]byte, error)
    26  }
    27  
    28  // InterfaceMarshaler interface has MarshalYAML compatible with github.com/go-yaml/yaml package.
    29  type InterfaceMarshaler interface {
    30  	MarshalYAML() (interface{}, error)
    31  }
    32  
    33  // InterfaceMarshalerContext interface use InterfaceMarshaler with context.Context.
    34  type InterfaceMarshalerContext interface {
    35  	MarshalYAML(context.Context) (interface{}, error)
    36  }
    37  
    38  // BytesUnmarshaler interface may be implemented by types to customize their
    39  // behavior when being unmarshaled from a YAML document.
    40  type BytesUnmarshaler interface {
    41  	UnmarshalYAML([]byte) error
    42  }
    43  
    44  // BytesUnmarshalerContext interface use BytesUnmarshaler with context.Context.
    45  type BytesUnmarshalerContext interface {
    46  	UnmarshalYAML(context.Context, []byte) error
    47  }
    48  
    49  // InterfaceUnmarshaler interface has UnmarshalYAML compatible with github.com/go-yaml/yaml package.
    50  type InterfaceUnmarshaler interface {
    51  	UnmarshalYAML(func(interface{}) error) error
    52  }
    53  
    54  // InterfaceUnmarshalerContext interface use InterfaceUnmarshaler with context.Context.
    55  type InterfaceUnmarshalerContext interface {
    56  	UnmarshalYAML(context.Context, func(interface{}) error) error
    57  }
    58  
    59  // MapItem is an item in a MapSlice.
    60  type MapItem struct {
    61  	Key, Value interface{}
    62  }
    63  
    64  // MapSlice encodes and decodes as a YAML map.
    65  // The order of keys is preserved when encoding and decoding.
    66  type MapSlice []MapItem
    67  
    68  // ToMap convert to map[interface{}]interface{}.
    69  func (s MapSlice) ToMap() map[interface{}]interface{} {
    70  	v := map[interface{}]interface{}{}
    71  	for _, item := range s {
    72  		v[item.Key] = item.Value
    73  	}
    74  	return v
    75  }
    76  
    77  // Marshal serializes the value provided into a YAML document. The structure
    78  // of the generated document will reflect the structure of the value itself.
    79  // Maps and pointers (to struct, string, int, etc) are accepted as the in value.
    80  //
    81  // Struct fields are only marshalled if they are exported (have an upper case
    82  // first letter), and are marshalled using the field name lowercased as the
    83  // default key. Custom keys may be defined via the "yaml" name in the field
    84  // tag: the content preceding the first comma is used as the key, and the
    85  // following comma-separated options are used to tweak the marshalling process.
    86  // Conflicting names result in a runtime error.
    87  //
    88  // The field tag format accepted is:
    89  //
    90  //	`(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
    91  //
    92  // The following flags are currently supported:
    93  //
    94  //	omitempty    Only include the field if it's not set to the zero
    95  //	             value for the type or to empty slices or maps.
    96  //	             Zero valued structs will be omitted if all their public
    97  //	             fields are zero, unless they implement an IsZero
    98  //	             method (see the IsZeroer interface type), in which
    99  //	             case the field will be included if that method returns true.
   100  //
   101  //	flow         Marshal using a flow style (useful for structs,
   102  //	             sequences and maps).
   103  //
   104  //	inline       Inline the field, which must be a struct or a map,
   105  //	             causing all of its fields or keys to be processed as if
   106  //	             they were part of the outer struct. For maps, keys must
   107  //	             not conflict with the yaml keys of other struct fields.
   108  //
   109  //	anchor       Marshal with anchor. If want to define anchor name explicitly, use anchor=name style.
   110  //	             Otherwise, if used 'anchor' name only, used the field name lowercased as the anchor name
   111  //
   112  //	alias        Marshal with alias. If want to define alias name explicitly, use alias=name style.
   113  //	             Otherwise, If omitted alias name and the field type is pointer type,
   114  //	             assigned anchor name automatically from same pointer address.
   115  //
   116  // In addition, if the key is "-", the field is ignored.
   117  //
   118  // For example:
   119  //
   120  //	type T struct {
   121  //	    F int `yaml:"a,omitempty"`
   122  //	    B int
   123  //	}
   124  //	yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
   125  //	yaml.Marshal(&T{F: 1}) // Returns "a: 1\nb: 0\n"
   126  func Marshal(v interface{}) ([]byte, error) {
   127  	return MarshalWithOptions(v)
   128  }
   129  
   130  // MarshalWithOptions serializes the value provided into a YAML document with EncodeOptions.
   131  func MarshalWithOptions(v interface{}, opts ...EncodeOption) ([]byte, error) {
   132  	return MarshalContext(context.Background(), v, opts...)
   133  }
   134  
   135  // MarshalContext serializes the value provided into a YAML document with context.Context and EncodeOptions.
   136  func MarshalContext(ctx context.Context, v interface{}, opts ...EncodeOption) ([]byte, error) {
   137  	var buf bytes.Buffer
   138  	if err := NewEncoder(&buf, opts...).EncodeContext(ctx, v); err != nil {
   139  		return nil, errors.Wrapf(err, "failed to marshal")
   140  	}
   141  	return buf.Bytes(), nil
   142  }
   143  
   144  // ValueToNode convert from value to ast.Node.
   145  func ValueToNode(v interface{}, opts ...EncodeOption) (ast.Node, error) {
   146  	var buf bytes.Buffer
   147  	node, err := NewEncoder(&buf, opts...).EncodeToNode(v)
   148  	if err != nil {
   149  		return nil, errors.Wrapf(err, "failed to convert value to node")
   150  	}
   151  	return node, nil
   152  }
   153  
   154  // Unmarshal decodes the first document found within the in byte slice
   155  // and assigns decoded values into the out value.
   156  //
   157  // Struct fields are only unmarshalled if they are exported (have an
   158  // upper case first letter), and are unmarshalled using the field name
   159  // lowercased as the default key. Custom keys may be defined via the
   160  // "yaml" name in the field tag: the content preceding the first comma
   161  // is used as the key, and the following comma-separated options are
   162  // used to tweak the marshalling process (see Marshal).
   163  // Conflicting names result in a runtime error.
   164  //
   165  // For example:
   166  //
   167  //	type T struct {
   168  //	    F int `yaml:"a,omitempty"`
   169  //	    B int
   170  //	}
   171  //	var t T
   172  //	yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
   173  //
   174  // See the documentation of Marshal for the format of tags and a list of
   175  // supported tag options.
   176  func Unmarshal(data []byte, v interface{}) error {
   177  	return UnmarshalWithOptions(data, v)
   178  }
   179  
   180  // UnmarshalWithOptions decodes with DecodeOptions the first document found within the in byte slice
   181  // and assigns decoded values into the out value.
   182  func UnmarshalWithOptions(data []byte, v interface{}, opts ...DecodeOption) error {
   183  	return UnmarshalContext(context.Background(), data, v, opts...)
   184  }
   185  
   186  // UnmarshalContext decodes with context.Context and DecodeOptions.
   187  func UnmarshalContext(ctx context.Context, data []byte, v interface{}, opts ...DecodeOption) error {
   188  	dec := NewDecoder(bytes.NewBuffer(data), opts...)
   189  	if err := dec.DecodeContext(ctx, v); err != nil {
   190  		if err == io.EOF {
   191  			return nil
   192  		}
   193  		return errors.Wrapf(err, "failed to unmarshal")
   194  	}
   195  	return nil
   196  }
   197  
   198  // NodeToValue converts node to the value pointed to by v.
   199  func NodeToValue(node ast.Node, v interface{}, opts ...DecodeOption) error {
   200  	var buf bytes.Buffer
   201  	if err := NewDecoder(&buf, opts...).DecodeFromNode(node, v); err != nil {
   202  		return errors.Wrapf(err, "failed to convert node to value")
   203  	}
   204  	return nil
   205  }
   206  
   207  // FormatError is a utility function that takes advantage of the metadata
   208  // stored in the errors returned by this package's parser.
   209  //
   210  // If the second argument `colored` is true, the error message is colorized.
   211  // If the third argument `inclSource` is true, the error message will
   212  // contain snippets of the YAML source that was used.
   213  func FormatError(e error, colored, inclSource bool) string {
   214  	var pp errors.PrettyPrinter
   215  	if xerrors.As(e, &pp) {
   216  		var buf bytes.Buffer
   217  		pp.PrettyPrint(&errors.Sink{&buf}, colored, inclSource)
   218  		return buf.String()
   219  	}
   220  
   221  	return e.Error()
   222  }
   223  
   224  // YAMLToJSON convert YAML bytes to JSON.
   225  func YAMLToJSON(bytes []byte) ([]byte, error) {
   226  	var v interface{}
   227  	if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil {
   228  		return nil, errors.Wrapf(err, "failed to unmarshal")
   229  	}
   230  	out, err := MarshalWithOptions(v, JSON())
   231  	if err != nil {
   232  		return nil, errors.Wrapf(err, "failed to marshal with json option")
   233  	}
   234  	return out, nil
   235  }
   236  
   237  // JSONToYAML convert JSON bytes to YAML.
   238  func JSONToYAML(bytes []byte) ([]byte, error) {
   239  	var v interface{}
   240  	if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil {
   241  		return nil, errors.Wrapf(err, "failed to unmarshal from json bytes")
   242  	}
   243  	out, err := Marshal(v)
   244  	if err != nil {
   245  		return nil, errors.Wrapf(err, "failed to marshal")
   246  	}
   247  	return out, nil
   248  }