github.com/hedzr/evendeep@v0.4.8/withopts.go (about)

     1  package evendeep
     2  
     3  import (
     4  	"encoding/json"
     5  
     6  	"github.com/hedzr/log/dir"
     7  
     8  	"github.com/hedzr/evendeep/flags"
     9  	"github.com/hedzr/evendeep/flags/cms"
    10  )
    11  
    12  // Opt options functor.
    13  type Opt func(c *cpController)
    14  
    15  // WithValueConverters gives a set of ValueConverter.
    16  // The value converters will be applied on its Match returns ok.
    17  func WithValueConverters(cvt ...ValueConverter) Opt {
    18  	return func(c *cpController) {
    19  		c.valueConverters = append(c.valueConverters, cvt...)
    20  	}
    21  }
    22  
    23  // WithValueCopiers gives a set of ValueCopier.
    24  // The value copiers will be applied on its Match returns ok.
    25  func WithValueCopiers(cvt ...ValueCopier) Opt {
    26  	return func(c *cpController) {
    27  		c.valueCopiers = append(c.valueCopiers, cvt...)
    28  	}
    29  }
    30  
    31  // WithTryApplyConverterAtFirst specifies which is first when
    32  // trying/applying ValueConverters and ValueCopiers.
    33  func WithTryApplyConverterAtFirst(b bool) Opt {
    34  	return func(c *cpController) {
    35  		c.tryApplyConverterAtFirst = b
    36  	}
    37  }
    38  
    39  // WithTryApplyConverterAtFirstOpt is shortcut of WithTryApplyConverterAtFirst(true).
    40  var WithTryApplyConverterAtFirstOpt = WithTryApplyConverterAtFirst(true) //nolint:gochecknoglobals //i know that
    41  
    42  // WithSourceValueExtractor specify a source field value extractor,
    43  // which will be applied on each field being copied to target.
    44  //
    45  // Just work for non-nested struct.
    46  //
    47  // For instance:
    48  //
    49  //	c := context.WithValue(context.TODO(), "Data", map[string]typ.Any{
    50  //		"A": 12,
    51  //	})
    52  //
    53  //	tgt := struct {
    54  //		A int
    55  //	}{}
    56  //
    57  //	evendeep.DeepCopy(c, &tgt,
    58  //	  evendeep.WithSourceValueExtractor(func(targetName string) typ.Any {
    59  //		if m, ok := c.Value("Data").(map[string]typ.Any); ok {
    60  //			return m[targetName]
    61  //		}
    62  //		return nil
    63  //	}))
    64  //
    65  //	if tgt.A != 12 {
    66  //		t.FailNow()
    67  //	}
    68  func WithSourceValueExtractor(e SourceValueExtractor) Opt {
    69  	return func(c *cpController) {
    70  		c.sourceExtractor = e
    71  	}
    72  }
    73  
    74  // WithTargetValueSetter _
    75  //
    76  // In the TargetValueSetter you could return evendeep.ErrShouldFallback to
    77  // call the evendeep standard processing.
    78  //
    79  // TargetValueSetter can work for struct and map.
    80  //
    81  // NOTE that the sourceNames[0] is current field name, and the whole
    82  // sourceNames slice includes the path of the nested struct(s),
    83  // in reversal order.
    84  //
    85  // For instance:
    86  //
    87  //	type srcS struct {
    88  //	  A int
    89  //	  B bool
    90  //	  C string
    91  //	}
    92  //
    93  //	src := &srcS{
    94  //	  A: 5,
    95  //	  B: true,
    96  //	  C: helloString,
    97  //	}
    98  //	tgt := map[string]typ.Any{
    99  //	  "Z": "str",
   100  //	}
   101  //
   102  //	err := evendeep.New().CopyTo(src, &tgt,
   103  //	  evendeep.WithTargetValueSetter(
   104  //	  func(value *reflect.Value, sourceNames ...string) (err error) {
   105  //	    if value != nil {
   106  //	      name := "Mo" + strings.Join(sourceNames, ".")
   107  //	      tgt[name] = value.Interface()
   108  //	    }
   109  //	    return // ErrShouldFallback to call the evendeep standard processing
   110  //	  }),
   111  //	)
   112  //
   113  //	if err != nil || tgt["MoA"] != 5 || tgt["MoB"] != true || tgt["MoC"] != helloString || tgt["Z"] != "str" {
   114  //	  t.Errorf("err: %v, tgt: %v", err, tgt)
   115  //	  t.FailNow()
   116  //	}
   117  func WithTargetValueSetter(e TargetValueSetter) Opt {
   118  	return func(c *cpController) {
   119  		c.targetSetter = e
   120  	}
   121  }
   122  
   123  // WithCloneStyle sets the cpController to clone mode.
   124  // In this mode, source object will be cloned to a new
   125  // object and returned as new target object.
   126  func WithCloneStyle() Opt {
   127  	return func(c *cpController) {
   128  		c.makeNewClone = true
   129  	}
   130  }
   131  
   132  // WithCopyStyle sets the cpController to copier mode.
   133  // In this mode, source object will be deepcopied to
   134  // target object.
   135  func WithCopyStyle() Opt {
   136  	return func(c *cpController) {
   137  		c.makeNewClone = false
   138  	}
   139  }
   140  
   141  // WithStrategies appends more flags into *cpController.
   142  //
   143  // For example:
   144  //
   145  //	WithStrategies(cms.OmitIfZero, cms.OmitIfNil, cms.OmitIfEmpty, cms.NoOmit)
   146  //	WithStrategies(cms.ClearIfMissed, cms.ClearIfInvalid)
   147  //	WithStrategies(cms.KeepIfNotEq, cms.ClearIfEq)
   148  func WithStrategies(flagsList ...cms.CopyMergeStrategy) Opt {
   149  	return func(c *cpController) {
   150  		if c.flags == nil {
   151  			c.flags = flags.New(flagsList...)
   152  		} else {
   153  			for _, f := range flagsList {
   154  				c.flags[f] = true
   155  			}
   156  		}
   157  	}
   158  }
   159  
   160  // WithCleanStrategies set the given flags into *cpController, older
   161  // flags will be clear at first.
   162  func WithCleanStrategies(flagsList ...cms.CopyMergeStrategy) Opt {
   163  	return func(c *cpController) {
   164  		c.flags = flags.New(flagsList...)
   165  	}
   166  }
   167  
   168  // WithByNameStrategyOpt is synonym of cms.ByName by calling WithCleanStrategies.
   169  //
   170  // If you're using WithByNameStrategyOpt and WithStrategies(...) at same time, please notes it
   171  // will clear any existent flags before setting.
   172  var WithByNameStrategyOpt = WithCleanStrategies(cms.ByName) //nolint:gochecknoglobals //i know that
   173  
   174  // WithByOrdinalStrategyOpt is synonym of cms.ByOrdinal by calling WithCleanStrategies.
   175  //
   176  // If you're using WithByOrdinalStrategyOpt and WithStrategies(...) at same time, please notes it
   177  // will clear any existent flags before setting.
   178  var WithByOrdinalStrategyOpt = WithCleanStrategies(cms.ByOrdinal) //nolint:gochecknoglobals //i know that
   179  
   180  // WithCopyStrategyOpt is synonym of cms.SliceCopy + cms.MapCopy by calling WithCleanStrategies.
   181  //
   182  // If you're using WithCopyStrategyOpt and WithStrategies(...) at same time, please notes it
   183  // will clear any existent flags before setting.
   184  var WithCopyStrategyOpt = WithCleanStrategies(cms.SliceCopy, cms.MapCopy) //nolint:gochecknoglobals //i know that
   185  
   186  // WithMergeStrategyOpt is synonym of cms.SliceMerge + cms.MapMerge by calling WithCleanStrategies.
   187  //
   188  // If you're using WithMergeStrategyOpt and WithStrategies(...) at same time, please notes it
   189  // will clear any existent flags before setting.
   190  var WithMergeStrategyOpt = WithCleanStrategies(cms.SliceMerge, cms.MapMerge) //nolint:gochecknoglobals //i know that
   191  
   192  // WithORMDiffOpt is synonym of cms.ClearIfEq + cms.KeepIfNotEq + cms.ClearIfInvalid by calling WithCleanStrategies.
   193  //
   194  // If you're using WithORMDiffOpt and WithStrategies(...) at same time, please notes it
   195  // will clear any existent flags before setting.
   196  var WithORMDiffOpt = WithCleanStrategies(cms.ClearIfEq, cms.KeepIfNotEq, cms.ClearIfInvalid) //nolint:gochecknoglobals,lll //i know that
   197  
   198  // WithOmitEmptyOpt is synonym of cms.OmitIfEmpty by calling Clean.
   199  //
   200  // If you're using WithOmitEmptyOpt and WithStrategies(...) at same time, please notes it
   201  // will clear any existent flags before setting.
   202  var WithOmitEmptyOpt = WithCleanStrategies(cms.OmitIfEmpty) //nolint:gochecknoglobals //i know that
   203  
   204  // WithStrategiesReset clears the exists flags in a *cpController.
   205  // So that you can append new ones (with WithStrategies(flags...)).
   206  //
   207  // In generally, WithStrategiesReset is synonym of cms.SliceCopy +
   208  // cms.MapCopy, since all strategies are cleared. A nothing Flags
   209  // means that a set of default strategies will be applied,
   210  // in other words, its include:
   211  //
   212  //	cms.Default, cms.NoOmit, cms.NoOmitTarget,
   213  //	cms.SliceCopy, cms.MapCopy,
   214  //	cms.ByOrdinal,
   215  //
   216  // If a flagsList supplied, WithStrategiesReset will add them and
   217  // set the state to false.
   218  func WithStrategiesReset(flagsList ...cms.CopyMergeStrategy) Opt {
   219  	return func(c *cpController) {
   220  		c.flags = flags.New(flagsList...)
   221  		for _, fx := range flagsList {
   222  			if _, ok := c.flags[fx]; ok {
   223  				c.flags[fx] = false
   224  			}
   225  		}
   226  	}
   227  }
   228  
   229  // WithAutoExpandForInnerStruct does copy fields with flat struct.
   230  // When autoExpandForInnerStruct is enabled, the iterator will go into
   231  // any embedded struct and traverse its fields with a flatten mode.
   232  //
   233  // For instance, the iteration on struct:
   234  //
   235  //	type A struct {
   236  //	   F1 string
   237  //	   F2 int
   238  //	}
   239  //	type B struct {
   240  //	   F1 bool
   241  //	   F2 A
   242  //	   F3 float32
   243  //	}
   244  //
   245  // will produce the sequences:
   246  //
   247  //	B.F1, B.F2, B.F2 - A.F1, B.F2 - A.F2, B.F3
   248  //
   249  // Default is true.
   250  func WithAutoExpandForInnerStruct(autoExpand bool) Opt {
   251  	return func(c *cpController) {
   252  		c.autoExpandStruct = autoExpand
   253  	}
   254  }
   255  
   256  // WithAutoExpandStructOpt is synonym of WithAutoExpandForInnerStruct(true).
   257  var WithAutoExpandStructOpt = WithAutoExpandForInnerStruct(true) //nolint:gochecknoglobals //i know that
   258  
   259  // WithAutoNewForStructField does create new instance on ptr field of a struct.
   260  //
   261  // When cloning to a new target object, it might be helpful.
   262  //
   263  // Default is true.
   264  func WithAutoNewForStructField(autoNew bool) Opt {
   265  	return func(c *cpController) {
   266  		c.autoNewStruct = autoNew
   267  	}
   268  }
   269  
   270  // WithAutoNewForStructFieldOpt is synonym of WithAutoNewForStructField(true).
   271  var WithAutoNewForStructFieldOpt = WithAutoNewForStructField(true) //nolint:gochecknoglobals //i know that
   272  
   273  // WithCopyUnexportedField try to copy the unexported fields
   274  // with special way.
   275  //
   276  // This feature needs unsafe package present.
   277  //
   278  // Default is true.
   279  func WithCopyUnexportedField(b bool) Opt {
   280  	return func(c *cpController) {
   281  		c.copyUnexportedFields = b
   282  	}
   283  }
   284  
   285  // WithCopyUnexportedFieldOpt is shortcut of WithCopyUnexportedField.
   286  var WithCopyUnexportedFieldOpt = WithCopyUnexportedField(true) //nolint:gochecknoglobals //i know that
   287  
   288  // WithCopyFunctionResultToTarget invoke source function member and
   289  // pass the result to the responsible target field.
   290  //
   291  // It just works when target field is acceptable.
   292  //
   293  // Default is true.
   294  func WithCopyFunctionResultToTarget(b bool) Opt {
   295  	return func(c *cpController) {
   296  		c.copyFunctionResultToTarget = b
   297  	}
   298  }
   299  
   300  // WithCopyFunctionResultToTargetOpt is shortcut of WithCopyFunctionResultToTarget.
   301  var WithCopyFunctionResultToTargetOpt = WithCopyFunctionResultToTarget(true) //nolint:gochecknoglobals //i know that
   302  
   303  // WithPassSourceToTargetFunction invoke target function member and
   304  // pass the source as its input parameters.
   305  //
   306  // Default is true.
   307  func WithPassSourceToTargetFunction(b bool) Opt {
   308  	return func(c *cpController) {
   309  		c.passSourceAsFunctionInArgs = b
   310  	}
   311  }
   312  
   313  // WithPassSourceToTargetFunctionOpt is shortcut of WithPassSourceToTargetFunction.
   314  var WithPassSourceToTargetFunctionOpt = WithPassSourceToTargetFunction(true) //nolint:gochecknoglobals //i know that
   315  
   316  // WithSyncAdvancing decides how to advance to next field especially
   317  // a source field had been ignored.
   318  // By default, (false), the target field won't be advanced while the
   319  // source field had been ignored.
   320  // For sync-advanced flag is true, the target field step to next.
   321  //
   322  // Just for cms.ByOrdinal mode.
   323  func WithSyncAdvancing(syncAdvancing bool) Opt {
   324  	return func(c *cpController) {
   325  		c.advanceTargetFieldPointerEvenIfSourceIgnored = syncAdvancing
   326  	}
   327  }
   328  
   329  // WithSyncAdvancingOpt is synonym of WithAutoExpandForInnerStruct(true).
   330  var WithSyncAdvancingOpt = WithSyncAdvancing(true) //nolint:gochecknoglobals //i know that
   331  
   332  // WithWipeTargetSliceFirst enables the option which assumes the target
   333  // Slice or Map will be wipe out at first before copying/merging from
   334  // source field.
   335  func WithWipeTargetSliceFirst(wipe bool) Opt {
   336  	return func(c *cpController) {
   337  		c.wipeSlice1st = wipe
   338  	}
   339  }
   340  
   341  // WithWipeTargetSliceFirstOpt is synonym of WithWipeTargetSliceFirst(true).
   342  var WithWipeTargetSliceFirstOpt = WithWipeTargetSliceFirst(true) //nolint:gochecknoglobals //i know that
   343  
   344  // WithIgnoreNames does specify the ignored field names list.
   345  //
   346  // Use the filename wildcard match characters (aka. '*' and '?', and '**')
   347  // as your advantages, the algor is isWildMatch() and dir.IsWildMatch.
   348  //
   349  // These patterns will only be tested on struct fields.
   350  func WithIgnoreNames(names ...string) Opt {
   351  	return func(c *cpController) {
   352  		c.ignoreNames = append(c.ignoreNames, names...)
   353  	}
   354  }
   355  
   356  // WithIgnoreNamesReset clear the ignored name list set.
   357  func WithIgnoreNamesReset() Opt {
   358  	return func(c *cpController) {
   359  		c.ignoreNames = nil
   360  	}
   361  }
   362  
   363  // isWildMatch provides a filename wildcard matching algorithm
   364  // by dir.IsWildMatch.
   365  func isWildMatch(s, pattern string) bool {
   366  	return dir.IsWildMatch(s, pattern)
   367  }
   368  
   369  // WithStructTagName set the name which is used for retrieve the struct tag pieces.
   370  //
   371  // Default is "copy", the corresponding struct with tag looks like:
   372  //
   373  //	type AFT struct {
   374  //	    flags     flags.Flags `copy:",cleareq"`
   375  //	    converter *ValueConverter
   376  //	    wouldbe   int `copy:",must,keepneq,omitzero,mapmerge"`
   377  //	    ignored1  int `copy:"-"`
   378  //	    ignored2  int `copy:",-"`
   379  //	}
   380  func WithStructTagName(name string) Opt {
   381  	return func(c *cpController) {
   382  		c.tagKeyName = name
   383  	}
   384  }
   385  
   386  // WithoutPanic disable panic() call internally.
   387  //
   388  // Default is true.
   389  func WithoutPanic() Opt {
   390  	return func(c *cpController) {
   391  		c.rethrow = false
   392  	}
   393  }
   394  
   395  // WithStringMarshaller provides a string marshaller which will
   396  // be applied when a map is going to be copied to string.
   397  //
   398  // Default is json marshaller.
   399  //
   400  // If BinaryMarshaler has been implemented, the source.Marshal() will
   401  // be applied.
   402  //
   403  // It's synonym of RegisterDefaultStringMarshaller.
   404  func WithStringMarshaller(m TextMarshaller) Opt {
   405  	return func(c *cpController) {
   406  		RegisterDefaultStringMarshaller(m)
   407  	}
   408  }
   409  
   410  // RegisterDefaultStringMarshaller provides a string marshaller which will
   411  // be applied when a map is going to be copied to string.
   412  //
   413  // Default is json marshaller (json.MarshalIndent).
   414  //
   415  // If encoding.TextMarshaler/json.Marshaler have been implemented, the
   416  // source.MarshalText/MarshalJSON() will be applied.
   417  //
   418  // It's synonym of WithStringMarshaller.
   419  func RegisterDefaultStringMarshaller(m TextMarshaller) {
   420  	if m == nil {
   421  		m = json.Marshal
   422  	}
   423  	textMarshaller = m
   424  }
   425  
   426  // TextMarshaller for string marshalling.
   427  type TextMarshaller func(v interface{}) ([]byte, error)