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  }