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)