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 }