github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/yaml/option.go (about) 1 package yaml 2 3 import ( 4 "io" 5 "reflect" 6 7 "github.com/bingoohuang/gg/pkg/yaml/ast" 8 "golang.org/x/xerrors" 9 ) 10 11 // DecodeOption functional option type for Decoder 12 type DecodeOption func(d *Decoder) error 13 14 var ErrContinue = xerrors.New("continue processing") 15 16 // DecoderFn is a prototype func for customized decoding of yaml values. 17 // If err returns ErrContinue, the decoding will continued after the func invoking. 18 type DecoderFn func(node ast.Node, typ reflect.Type) (reflect.Value, error) 19 20 // TypeDecoder registers a decoder associated with a struct field type. 21 func TypeDecoder(typ reflect.Type, f DecoderFn) DecodeOption { 22 return func(d *Decoder) error { 23 d.typeDecoderMap[typ] = f 24 return nil 25 } 26 } 27 28 // LabelDecoder registers a decoder associated with a label defined in the struct tag like 'yaml:"name,label=xyz". 29 func LabelDecoder(label string, f DecoderFn) DecodeOption { 30 return func(d *Decoder) error { 31 d.labelDecoderMap[label] = f 32 return nil 33 } 34 } 35 36 // ReferenceReaders pass to Decoder that reference to anchor defined by passed readers 37 func ReferenceReaders(readers ...io.Reader) DecodeOption { 38 return func(d *Decoder) error { 39 d.referenceReaders = append(d.referenceReaders, readers...) 40 return nil 41 } 42 } 43 44 // ReferenceFiles pass to Decoder that reference to anchor defined by passed files 45 func ReferenceFiles(files ...string) DecodeOption { 46 return func(d *Decoder) error { 47 d.referenceFiles = files 48 return nil 49 } 50 } 51 52 // ReferenceDirs pass to Decoder that reference to anchor defined by files under the passed dirs 53 func ReferenceDirs(dirs ...string) DecodeOption { 54 return func(d *Decoder) error { 55 d.referenceDirs = dirs 56 return nil 57 } 58 } 59 60 // RecursiveDir search yaml file recursively from passed dirs by ReferenceDirs option 61 func RecursiveDir(isRecursive bool) DecodeOption { 62 return func(d *Decoder) error { 63 d.isRecursiveDir = isRecursive 64 return nil 65 } 66 } 67 68 // Validator set StructValidator instance to Decoder 69 func Validator(v StructValidator) DecodeOption { 70 return func(d *Decoder) error { 71 d.validator = v 72 return nil 73 } 74 } 75 76 // Strict enable DisallowUnknownField and DisallowDuplicateKey 77 func Strict() DecodeOption { 78 return func(d *Decoder) error { 79 d.disallowUnknownField = true 80 d.disallowDuplicateKey = true 81 return nil 82 } 83 } 84 85 // DisallowUnknownField causes the Decoder to return an error when the destination 86 // is a struct and the input contains object keys which do not match any 87 // non-ignored, exported fields in the destination. 88 func DisallowUnknownField() DecodeOption { 89 return func(d *Decoder) error { 90 d.disallowUnknownField = true 91 return nil 92 } 93 } 94 95 // DisallowDuplicateKey causes an error when mapping keys that are duplicates 96 func DisallowDuplicateKey() DecodeOption { 97 return func(d *Decoder) error { 98 d.disallowDuplicateKey = true 99 return nil 100 } 101 } 102 103 // UseOrderedMap can be interpreted as a map, 104 // and uses MapSlice ( ordered map ) aggressively if there is no type specification 105 func UseOrderedMap() DecodeOption { 106 return func(d *Decoder) error { 107 d.useOrderedMap = true 108 return nil 109 } 110 } 111 112 // UseJSONUnmarshaler if neither `BytesUnmarshaler` nor `InterfaceUnmarshaler` is implemented 113 // and `UnmashalJSON([]byte)error` is implemented, convert the argument from `YAML` to `JSON` and then call it. 114 func UseJSONUnmarshaler() DecodeOption { 115 return func(d *Decoder) error { 116 d.useJSONUnmarshaler = true 117 return nil 118 } 119 } 120 121 // EncodeOption functional option type for Encoder 122 type EncodeOption func(e *Encoder) error 123 124 // KeyNaming changes the yaml keys' naming when encoding structs. 125 func KeyNaming(v KeyNamingStrategy) EncodeOption { 126 return func(e *Encoder) error { 127 e.KeyNaming = v 128 return nil 129 } 130 } 131 132 // Indent change indent number 133 func Indent(spaces int) EncodeOption { 134 return func(e *Encoder) error { 135 e.indent = spaces 136 return nil 137 } 138 } 139 140 // IndentSequence causes sequence values to be indented the same value as Indent 141 func IndentSequence(indent bool) EncodeOption { 142 return func(e *Encoder) error { 143 e.indentSequence = indent 144 return nil 145 } 146 } 147 148 // Flow encoding by flow style 149 func Flow(isFlowStyle bool) EncodeOption { 150 return func(e *Encoder) error { 151 e.isFlowStyle = isFlowStyle 152 return nil 153 } 154 } 155 156 // UseLiteralStyleIfMultiline causes encoding multiline strings with a literal syntax, 157 // no matter what characters they include 158 func UseLiteralStyleIfMultiline(useLiteralStyleIfMultiline bool) EncodeOption { 159 return func(e *Encoder) error { 160 e.useLiteralStyleIfMultiline = useLiteralStyleIfMultiline 161 return nil 162 } 163 } 164 165 // JSON encode in JSON format 166 func JSON() EncodeOption { 167 return func(e *Encoder) error { 168 e.isJSONStyle = true 169 e.isFlowStyle = true 170 return nil 171 } 172 } 173 174 // MarshalAnchor call back if encoder find an anchor during encoding 175 func MarshalAnchor(callback func(*ast.AnchorNode, interface{}) error) EncodeOption { 176 return func(e *Encoder) error { 177 e.anchorCallback = callback 178 return nil 179 } 180 } 181 182 // UseJSONMarshaler if neither `BytesMarshaler` nor `InterfaceMarshaler` 183 // nor `encoding.TextMarshaler` is implemented and `MarshalJSON()([]byte, error)` is implemented, 184 // call `MarshalJSON` to convert the returned `JSON` to `YAML` for processing. 185 func UseJSONMarshaler() EncodeOption { 186 return func(e *Encoder) error { 187 e.useJSONMarshaler = true 188 return nil 189 } 190 } 191 192 // CommentPosition type of the position for comment. 193 type CommentPosition int 194 195 const ( 196 CommentLinePosition CommentPosition = iota 197 CommentHeadPosition 198 ) 199 200 func (p CommentPosition) String() string { 201 switch p { 202 case CommentLinePosition: 203 return "Line" 204 case CommentHeadPosition: 205 return "Head" 206 default: 207 return "" 208 } 209 } 210 211 // LineComment create a one-line comment for CommentMap. 212 func LineComment(text string) *Comment { 213 return &Comment{ 214 Texts: []string{text}, 215 Position: CommentLinePosition, 216 } 217 } 218 219 // HeadComment create a multiline comment for CommentMap. 220 func HeadComment(texts ...string) *Comment { 221 return &Comment{ 222 Texts: texts, 223 Position: CommentHeadPosition, 224 } 225 } 226 227 // Comment raw data for comment. 228 type Comment struct { 229 Texts []string 230 Position CommentPosition 231 } 232 233 // CommentMap map of the position of the comment and the comment information. 234 type CommentMap map[string]*Comment 235 236 // WithComment add a comment using the location and text information given in the CommentMap. 237 func WithComment(cm CommentMap) EncodeOption { 238 return func(e *Encoder) error { 239 commentMap := map[*Path]*Comment{} 240 for k, v := range cm { 241 path, err := PathString(k) 242 if err != nil { 243 return err 244 } 245 commentMap[path] = v 246 } 247 e.commentMap = commentMap 248 return nil 249 } 250 }