github.com/hedzr/evendeep@v0.4.8/README.md (about)

     1  # even-deep
     2  
     3  ![Go](https://github.com/hedzr/evendeep/workflows/Go/badge.svg)
     4  [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/hedzr/evendeep.svg?label=release)](https://github.com/hedzr/evendeep/releases)
     5  [![go.dev](https://img.shields.io/badge/go.dev-reference-green)](https://pkg.go.dev/github.com/hedzr/evendeep)
     6  [![Go Report Card](https://goreportcard.com/badge/github.com/hedzr/evendeep)](https://goreportcard.com/report/github.com/hedzr/evendeep)
     7  [![codecov](https://codecov.io/gh/hedzr/evendeep/branch/master/graph/badge.svg)](https://codecov.io/gh/hedzr/evendeep)
     8  [![Coverage Status](https://coveralls.io/repos/github/hedzr/evendeep/badge.svg?branch=master)](https://coveralls.io/github/hedzr/evendeep?branch=master)
     9  
    10  Per-field copying deeply, and comparing deeply abilities.
    11  
    12  This library is designed for making everything customizable.
    13  
    14  ## Features
    15  
    16  - loosely and reasonable data-types conversions, acrossing primitives, composites and functions, with customizable
    17    converters/transformers
    18  - unexported values (optional), ...
    19  - circular references immunization
    20  - fully customizable
    21    - user-defined value/type converters/transformers
    22    - user-defined field to field name converting rule via struct Tag
    23  - easily apply different strategies
    24    - basic strategies are: copy-n-merge, clone,
    25    - strategies per struct field:
    26      `slicecopy`, `slicemerge`, `mapcopy`, `mapmerge`,
    27      `omitempty` (keep if source is zero or nil), `omitnil`, `omitzero`,
    28      `keepneq` (keep if not equal), `cleareq` (clear if equal), ...
    29  - copy fields by name or ordinal
    30    - field to field
    31    - field to method, method to field
    32    - value to function (as input), function result to value
    33    - slice[0] to struct, struct to slice[0]
    34    - struct to map, map to struct
    35    - User-defined extractor/getter on various source
    36    - User-defined setter for struct or map target (if mapkey is string)
    37    - ...
    38  
    39  - The deep series
    40    - deepcopy: [`DeepCopy()`](https://github.com/hedzr/evendeep/blob/master/deepcopy.go#L20),
    41      or [`New()`](https://github.com/hedzr/evendeep/blob/master/deepcopy.go#L110)
    42    - deepclone:[`MakeClone()`](https://github.com/hedzr/evendeep/blob/master/deepcopy.go#L36)
    43    - deepequal: [`DeepEqual()`](https://github.com/hedzr/evendeep/blob/master/equal.go#L13)
    44    - deepdiff: [`DeepDiff()`](https://github.com/hedzr/evendeep/blob/master/diff.go#L13)
    45  
    46  - Compatibilities
    47    - Run for Go Modules enabled (go1.11+)
    48  
    49  ## History
    50  
    51  - v0.4.8
    52    - fixed: check unexported field recursively now
    53    - improved some lines for better context logging in debugging
    54    - little changes
    55  
    56  - v0.4.7
    57    - upgrade deps
    58  
    59  - v0.4.3
    60    - fixed sometimes a ptr to new slice has not been cleaned in time
    61  
    62  - v0.4.1
    63    - public `dbglog` subpackage, added Err/Wrn/Colored
    64    - added ability to disable dbglog.Log at runtime
    65    - improved internal functions (tool.Valfmt, cl.SetUnexportedFieldIfMap, ...)
    66    - improved dbglog.Log outputting
    67    - fixed bugs
    68  
    69  - v0.4.0
    70    - fixed autonew when copying to nil member
    71    - improved diff on chan
    72    - better logging (verbose) with colors
    73  
    74  - v0.3.1
    75    - changed: `dbglog.LogValid` is constant now
    76    - improved code style
    77    - DeepCopy:
    78      - passing nil parameters can return safely without panic any more
    79    - DeepDiff:
    80      - imp/fea: `diff.WithStripPointerAtFirst` - locate the final objects and compare them
    81      - imp/fea: `diff.WithTreatEmptyStructPtrAsNilPtr` - when comparing two pointers in struct field loop, assume nil and pointer to an empty struct is identical
    82      - imp/fea: `diff.WithCompareDifferentTypeStructs` - you can compare two struct with different type, their fields could be `diff` by its name
    83      - imp/fea: `diff.WithIgnoreUnmatchedFields` - this is default option for `diff.WithCompareDifferentTypeStructs(true)` mode, the field names unmatched couldn't take effects to comparing result
    84      - imp/fea: `diff.WithCompareDifferentSizeArrays` - `[2]string{"1","2"}` and `[3]string{"1","2",<empty>}` can be assumed as identity
    85      - By default,
    86        - they're assumed as identity: nil and zero array/map.
    87        - they're not identical: nil ptr to struct, and ptr to empty struct (can be overridden by `WithTreatEmptyStructPtrAsNilPtr`).
    88        - the slice elements' order is sensitive, except you're `diff` with `WithSliceOrderedComparison(true)`.
    89  
    90  - More in [CHANGELOG](https://github.com/hedzr/evendeep/blob/master/CHANGELOG)
    91  
    92  ## Usages
    93  
    94  ### deepcopy
    95  
    96  `eventdeep.New`, `eventdeep.MakeClone` and `eventdeep.DeepCopy` are main entries.
    97  
    98  By default, `DeepCopy()` will copy and **merge** source into destination object. That means, a map or a slice will be merged
    99  deeply, same to a struct.
   100  
   101  [`New(opts...)`](https://github.com/hedzr/evendeep/blob/master/deepcopy.go#L110) gives a most even scalable interface
   102  than `DeepCopy`, it returns a new `DeepCopier` different to `DefaultCopyController` and you can make call
   103  to `DeepCopier.DeepCopy(old, new, opts...)`.
   104  
   105  In copy-n-merge mode, copying `[2, 3]` to `[3, 7]` will get `[3, 7, 2]`.
   106  
   107  #### Getting Started
   108  
   109  Here is a basic sample code:
   110  
   111  ```go
   112  func TestExample1(t *testing.T) {
   113    timeZone, _ := time.LoadLocation("America/Phoenix")
   114    tm := time.Date(1999, 3, 13, 5, 57, 11, 1901, timeZone)
   115    src := eventdeep.Employee2{
   116      Base: eventdeep.Base{
   117        Name:      "Bob",
   118        Birthday:  &tm,
   119        Age:       24,
   120        EmployeID: 7,
   121      },
   122      Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1",
   123      Image:  []byte{95, 27, 43, 66, 0, 21, 210},
   124      Attr:   &eventdeep.Attr{Attrs: []string{"hello", "world"}},
   125      Valid:  true,
   126    }
   127    var dst eventdeep.User
   128  
   129    // direct way but no error report: eventdeep.DeepCopy(src, &dst)
   130    c := eventdeep.New()
   131    if err := c.CopyTo(src, &dst); err != nil {
   132      t.Fatal(err)
   133    }
   134    if !reflect.DeepEqual(dst, eventdeep.User{
   135      Name:      "Bob",
   136      Birthday:  &tm,
   137      Age:       24,
   138      EmployeID: 7,
   139      Avatar:    "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1",
   140      Image:     []byte{95, 27, 43, 66, 0, 21, 210},
   141      Attr:      &eventdeep.Attr{Attrs: []string{"hello", "world"}},
   142      Valid:     true,
   143    }) {
   144      t.Fatalf("bad, got %v", dst)
   145    }
   146  }
   147  ```
   148  
   149  #### Customizing The Field Extractor
   150  
   151  For the unconventional deep copy, we can copy field to field via a source extractor.
   152  
   153  You need a target struct at first.
   154  
   155  ```go
   156  func TestStructWithSourceExtractor(t *testing.T) {
   157    c := context.WithValue(context.TODO(), "Data", map[string]typ.Any{
   158      "A": 12,
   159    })
   160  
   161    tgt := struct {
   162      A int
   163    }{}
   164  
   165    evendeep.DeepCopy(c, &tgt, evendeep.WithSourceValueExtractor(func(name string) typ.Any {
   166      if m, ok := c.Value("Data").(map[string]typ.Any); ok {
   167        return m[name]
   168      }
   169      return nil
   170    }))
   171  
   172    if tgt.A != 12 {
   173      t.FailNow()
   174    }
   175  }
   176  ```
   177  
   178  #### Customizing The Target Setter
   179  
   180  As a contrary, you might specify a setter to handle the setting action on copying struct and/or map.
   181  
   182  ```go
   183  func TestStructWithTargetSetter(t *testing.T) {
   184    type srcS struct {
   185      A int
   186      B bool
   187      C string
   188    }
   189  
   190    src := &srcS{
   191      A: 5,
   192      B: true,
   193      C: "helloString",
   194    }
   195    tgt := map[string]typ.Any{
   196      "Z": "str",
   197    }
   198  
   199    err := evendeep.New().CopyTo(src, &tgt,
   200      evendeep.WithTargetValueSetter(func(value *reflect.Value, sourceNames ...string) (err error) {
   201        if value != nil {
   202          name := "Mo" + strings.Join(sourceNames, ".")
   203          tgt[name] = value.Interface()
   204        }
   205        return // ErrShouldFallback to call the evendeep standard processing
   206      }),
   207    )
   208  
   209    if err != nil || tgt["MoA"] != 5 || tgt["MoB"] != true || tgt["MoC"] != "helloString" || tgt["Z"] != "str" {
   210      t.Errorf("err: %v, tgt: %v", err, tgt)
   211      t.FailNow()
   212    }
   213  }
   214  ```
   215  
   216  NOTE that the feature is only fit for copying on/between struct and/or map.
   217  
   218  If you really wanna customize the setter for primitives or others, concern to implement a ValueCopier or ValueConverter.
   219  
   220  #### `ByOrdinal` or `ByName`
   221  
   222  `evendeep` enumerates fields in struct/map/slice with two strategies: `ByOrdinal` and `ByName`.
   223  
   224  1. Default `ByOrdinal` assumes the copier loops all source fields and copy them to the corresponding destination with
   225     the ordinal order.
   226  2. `ByName` strategy assumes the copier loops all target fields, and try copying value from the coressponding source
   227     field by its name.
   228  
   229  When a name conversion rule is defined in a struct field tag, the copier will look for the name and copy value to, even
   230  if it's in `ByOrdinal` mode.
   231  
   232  #### Customizing A Converter
   233  
   234  The customized Type/Value Converter can be applied on transforming the data from source. For more information take a
   235  look [`ValueConverter`](https://github.com/hedzr/evendeep/blob/master/cvts.go#L127)
   236  and [`ValueCopier`](https://github.com/hedzr/evendeep/blob/master/cvts.go#L133). Its take effects on checking the value
   237  type of target or source, or both of them.
   238  
   239  ```go
   240  type MyType struct {
   241    I int
   242  }
   243  
   244  type MyTypeToStringConverter struct{}
   245  
   246  // Uncomment this line if you wanna implment a ValueCopier implementation too: 
   247  // func (c *MyTypeToStringConverter) CopyTo(ctx *eventdeep.ValueConverterContext, source, target reflect.Value) (err error) { return }
   248  
   249  func (c *MyTypeToStringConverter) Transform(ctx *eventdeep.ValueConverterContext, source reflect.Value, targetType reflect.Type) (target reflect.Value, err error) {
   250    if source.IsValid() && targetType.Kind() == reflect.String {
   251      var str string
   252      if str, err = eventdeep.FallbackToBuiltinStringMarshalling(source); err == nil {
   253        target = reflect.ValueOf(str)
   254      }
   255    }
   256    return
   257  }
   258  
   259  func (c *MyTypeToStringConverter) Match(params *eventdeep.Params, source, target reflect.Type) (ctx *eventdeep.ValueConverterContext, yes bool) {
   260    sn, sp := source.Name(), source.PkgPath()
   261    sk, tk := source.Kind(), target.Kind()
   262    if yes = sk == reflect.Struct && tk == reflect.String &&
   263      sn == "MyType" && sp == "github.com/hedzr/eventdeep_test"; yes {
   264      ctx = &eventdeep.ValueConverterContext{Params: params}
   265    }
   266    return
   267  }
   268  
   269  func TestExample2(t *testing.T) {
   270    var myData = MyType{I: 9}
   271    var dst string
   272    eventdeep.DeepCopy(myData, &dst, eventdeep.WithValueConverters(&MyTypeToStringConverter{}))
   273    if dst != `{
   274    "I": 9
   275  }` {
   276      t.Fatalf("bad, got %v", dst)
   277    }
   278  }
   279  ```
   280  
   281  Instead of `WithValueConverters` / `WithValueCopiers` for each times invoking `New()`, you might register yours once by
   282  calling `RegisterDefaultConverters` / `RegisterDefaultCopiers` into global registry.
   283  
   284  ```go
   285    // a stub call for coverage
   286    eventdeep.RegisterDefaultCopiers()
   287  
   288    var dst1 string
   289    eventdeep.RegisterDefaultConverters(&MyTypeToStringConverter{})
   290    eventdeep.DeepCopy(myData, &dst1)
   291    if dst1 != `{
   292    "I": 9
   293  }` {
   294      t.Fatalf("bad, got %v", dst)
   295    }
   296  ```
   297  
   298  #### Zero Target Fields If Equals To Source
   299  
   300  When we compare two Struct, the target one can be clear to zero except a field value is not equal to source field. This
   301  feature can be used for your ORM codes: someone loads a record as a golang struct variable, and make some changes, and
   302  invoking `eventdeep.DeepCopy(originRec, &newRecord, eventdeep.WithORMDiffOpt)`, the changes will be kept in `newRecord`
   303  and the others unchanged fields be cleanup at last.
   304  
   305  The codes are:
   306  
   307  ```go
   308  func TestExample3(t *testing.T) {
   309    timeZone, _ := time.LoadLocation("America/Phoenix")
   310    tm := time.Date(1999, 3, 13, 5, 57, 11, 1901, timeZone)
   311    var originRec = eventdeep.User{ ... }
   312    var newRecord eventdeep.User
   313    var t0 = time.Unix(0, 0)
   314    var expectRec = eventdeep.User{Name: "Barbara", Birthday: &t0, Attr: &eventdeep.Attr{}}
   315  
   316    eventdeep.DeepCopy(originRec, &newRecord)
   317    t.Logf("newRecord: %v", newRecord)
   318  
   319    newRecord.Name = "Barbara"
   320    eventdeep.DeepCopy(originRec, &newRecord, eventdeep.WithORMDiffOpt)
   321    ...
   322    if !reflect.DeepEqual(newRecord, expectRec) {
   323      t.Fatalf("bad, got %v | %v", newRecord, newRecord.Birthday.Nanosecond())
   324    }
   325  }
   326  ```
   327  
   328  #### Keep The Target Value If Source Is Empty
   329  
   330  Sometimes we would look for a do-not-modify copier, it'll keep the value of target fields while the corresponding source
   331  field is empty (zero or nil). Use `eventdeep.WithOmitEmptyOpt` in the case.
   332  
   333  ```go
   334  func TestExample4(t *testing.T) {
   335    timeZone, _ := time.LoadLocation("America/Phoenix")
   336    tm := time.Date(1999, 3, 13, 5, 57, 11, 1901, timeZone)
   337    var originRec = eventdeep.User{
   338      Name:      "Bob",
   339      Birthday:  &tm,
   340      Age:       24,
   341      EmployeID: 7,
   342      Avatar:    "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1",
   343      Image:     []byte{95, 27, 43, 66, 0, 21, 210},
   344      Attr:      &eventdeep.Attr{Attrs: []string{"hello", "world"}},
   345      Valid:     true,
   346    }
   347    var dstRecord eventdeep.User
   348    var t0 = time.Unix(0, 0)
   349    var emptyRecord = eventdeep.User{Name: "Barbara", Birthday: &t0}
   350    var expectRecord = eventdeep.User{Name: "Barbara", Birthday: &t0,
   351      Image: []byte{95, 27, 43, 66, 0, 21, 210},
   352      Attr:  &eventdeep.Attr{Attrs: []string{"hello", "world"}},
   353      Valid: true,
   354    }
   355  
   356    // prepare a hard copy at first
   357    eventdeep.DeepCopy(originRec, &dstRecord)
   358    t.Logf("dstRecord: %v", dstRecord)
   359  
   360    // now update dstRecord with the non-empty fields.
   361    eventdeep.DeepCopy(emptyRecord, &dstRecord, eventdeep.WithOmitEmptyOpt)
   362    t.Logf("dstRecord: %v", dstRecord)
   363    if !reflect.DeepEqual(dstRecord, expectRecord) {
   364      t.Fatalf("bad, got %v\nexpect: %v", dstRecord, expectRecord)
   365    }
   366  }
   367  ```
   368  
   369  #### String Marshalling
   370  
   371  While copying struct, map, slice, or other source to target string, the builtin `toStringConverter` will be launched.
   372  And the default logic includes marshaling the structural source to string, typically `json.Marshal`.
   373  
   374  This marshaller can be customized: `RegisterStringMarshaller` and `WithStringMarshaller` enable it:
   375  
   376  ```go
   377  eventdeep.RegisterStringMarshaller(yaml.Marshal)
   378  eventdeep.RegisterStringMarshaller(json.Marshal)
   379  ```
   380  
   381  The default marshaler is a wraper to `json.MarshalIndent`.
   382  
   383  #### Specify CopyMergeStrategy via struct Tag
   384  
   385  Sample struct is (use `copy` as key):
   386  
   387  ```go
   388  type AFT struct {
   389    flags     flags.Flags `copy:",cleareq"`
   390    converter *ValueConverter
   391    wouldbe   int `copy:",must,keepneq,omitzero,mapmerge"`
   392    ignored1 int `copy:"-"`
   393    ignored2 int `copy:",-"`
   394  }
   395  ```
   396  
   397  ##### Name conversions
   398  
   399  `copy` tag has form: `nameConversion[,strategies...]`. `nameConversion` gives a target field Name to define a name
   400  conversion strategy, or `-` to ignore the field.
   401  
   402  > `nameConversion` has form:
   403  >
   404  > - `-`: field is ignored
   405  > - `targetName`
   406  > - `->targetName`
   407  > - `sourceName->targetName`
   408  >
   409  > Spaces besides of `->` are allowed.
   410  
   411  Copier will check target field tag at first, and following by a source field tag checking.
   412  
   413  You may specify converting rule at either target or source side, Copier assume the target one is prior.
   414  
   415  **NOTE**: `nameConversion` is fully functional only for `cms.ByName` mode. It get partial work in `cms.ByOrdinal` mode (
   416  default mode).
   417  
   418  *TODO*: In `cms.ByOrdinal` (`*`) mode, a name converter can be applied in copying field to field.
   419  
   420  ##### Sample codes
   421  
   422  The test gives a sample to show you how the name-conversion and member function work together:
   423  
   424  ```go
   425  func TestStructWithNameConversions(t *testing.T) {
   426    type srcS struct {
   427      A int    `copy:"A1"`
   428      B bool   `copy:"B1,std"`
   429      C string `copy:"C1,"`
   430    }
   431  
   432    type dstS struct {
   433      A1 int
   434      B1 bool
   435      C1 string
   436    }
   437  
   438    src := &srcS{A: 6, B: true, C: "hello"}
   439    var tgt = dstS{A1: 1}
   440  
   441    // use ByName strategy,
   442    err := evendeep.New().CopyTo(src, &tgt, evendeep.WithByNameStrategyOpt)
   443  
   444    if tgt.A1 != 6 || !tgt.B1 || tgt.C1 != "hello" || err != nil {
   445      t.Fatalf("BAD COPY, tgt: %+v", tgt)
   446    }
   447  }
   448  ```
   449  
   450  #### Strategy Names
   451  
   452  The available tag names are (Almost newest, see its
   453  in [flags/cms/copymergestrategy.go](https://github.com/hedzr/evendeep/blob/master/flags/cms/copymergestrategy.go#L23)):
   454  
   455  | Tag name           | Flags                   | Detail                                           |
   456  | ------------------ | ----------------------- | ------------------------------------------------ |
   457  | `-`                | `cms.Ignore`            | field will be ignored                            |
   458  | `std` (*)          | `cms.Default`           | reserved                                         |
   459  | `must`             | `cms.Must`              | reserved                                         |
   460  | `cleareq`          | `cms.ClearIfEqual`      | set zero if target equal to source               |
   461  | `keepneq`          | `cms.KeepIfNotEq`       | don't copy source if target not equal to source  |
   462  | `clearinvalid`     | `cms.ClearIfInvalid`    | if target field is invalid, set to zero value    |
   463  | `noomit` (*)       | `cms.NoOmit`            |                                                  |
   464  | `omitempty`        | `cms.OmitIfEmpty`       | if source field is empty, keep destination value |
   465  | `omitnil`          | `cms.OmitIfNil`         |                                                  |
   466  | `omitzero`         | `cms.OmitIfZero`        |                                                  |
   467  | `noomittarget` (*) | `cms.NoOmitTarget`      |                                                  |
   468  | `omitemptytarget`  | `cms.OmitIfTargetEmpty` | if target field is empty, don't copy from source |
   469  | `omitniltarget`    | `cms.OmitIfTargetNil`   |                                                  |
   470  | `omitzerotarget`   | `cms.OmitIfTargetZero`  |                                                  |
   471  | `slicecopy`        | `cms.SliceCopy`         | copy elem by subscription                        |
   472  | `slicecopyappend`  | `cms.SliceCopyAppend`   | and append more                                  |
   473  | `slicemerge`       | `cms.SliceMerge`        | merge with order-insensitive                     |
   474  | `mapcopy`          | `cms.MapCopy`           | copy elem by key                                 |
   475  | `mapmerge`         | `cms.MapMerge`          | merge map deeply                                 |
   476  | ...                |                         |                                                  |
   477  
   478  > `*`: the flag is on by default.
   479  
   480  #### Notes About `DeepCopy()`
   481  
   482  Many settings are accumulated in multiple calling on `DeepCopy()`, such as `converters`, `ignoreNames`, and so on. The
   483  underlying object is `DefaultCopyController`.
   484  
   485  To get a fresh clean copier, `New()` or `NewFlatDeepCopier()` are the choices. BTW,
   486  sometimes `evendeep.ResetDefaultCopyController()` might be helpful.
   487  
   488  The only exception is copy-n-merge strategies. There flags are saved and restored on each calling on `DeepCopy()`.
   489  
   490  #### Notes About Global Settings
   491  
   492  Some settings are global and available to both of `DeepCopy()` and `New().CopyTo()`, such as:
   493  
   494  1. `WithStringMarshaller` or `RegisterDefaultStringMarshaller()`
   495  2. `RegisterDefaultConverters`
   496  3. `RegisterDefaultCopiers`
   497  
   498  And so on.
   499  
   500  ### deepdiff
   501  
   502  `DeepDiff` can deeply print the differences about two objects.
   503  
   504  ```go
   505  delta, equal := evendeep.DeepDiff([]int{3, 0, 9}, []int{9, 3, 0}, diff.WithSliceOrderedComparison(true))
   506  t.Logf("delta: %v", delta) // ""
   507  
   508  delta, equal := evendeep.DeepDiff([]int{3, 0}, []int{9, 3, 0}, diff.WithSliceOrderedComparison(true))
   509  t.Logf("delta: %v", delta) // "added: [0] = 9\n"
   510  
   511  delta, equal := evendeep.DeepDiff([]int{3, 0}, []int{9, 3, 0})
   512  t.Logf("delta: %v", delta)
   513  // Outputs:
   514  //   added: [2] = <zero>
   515  //   modified: [0] = 9 (int) (Old: 3)
   516  //   modified: [1] = 3 (int) (Old: <zero>)
   517  
   518  ```
   519  
   520  `DeepDiff` is a rewrote version
   521  upon [d4l3k/messagediff]([d4l3k/messagediff at v1.2.1 (github.com)](https://github.com/d4l3k/messagediff)). This new
   522  code enables user-defined comparer for you.
   523  
   524  #### Ignored Names
   525  
   526  [`diff.WithIgnoredFields(names...)`](https://github.com/hedzr/evendeep/blob/master/diff/diff.go#L41) can give a list of
   527  names which should be ignored when comparing.
   528  
   529  #### Slice-Order Insensitive
   530  
   531  In normal mode, `diff` is slice-order-sensitive, that means, `[1, 2] != [2, 1]`
   532  . [`WithSliceOrderedComparison(b bool)`](https://github.com/hedzr/evendeep/blob/master/diff/diff.go#L41) can unmind the
   533  differences of order and as an equal.
   534  
   535  #### Customizing Comparer
   536  
   537  For example, `evendeep` ships a `timeComparer`:
   538  
   539  ```go
   540  type timeComparer struct{}
   541  
   542  func (c *timeComparer) Match(typ reflect.Type) bool {
   543    return typ.String() == "time.Time"
   544  }
   545  
   546  func (c *timeComparer) Equal(ctx Context, lhs, rhs reflect.Value, path Path) (equal bool) {
   547    aTime := lhs.Interface().(time.Time)
   548    bTime := rhs.Interface().(time.Time)
   549    if equal = aTime.Equal(bTime); !equal {
   550      ctx.PutModified(ctx.PutPath(path), Update{Old: aTime.String(), New: bTime.String(), Typ: typfmtlite(&lhs)})
   551    }
   552    return
   553  }
   554  ```
   555  
   556  And it has been initialized into diff info struct. `timeComparer` provides a semantic comparing for `time.Time` objects.
   557  
   558  To enable your comparer,
   559  use [`diff.WithComparer(comparer)`](https://github.com/hedzr/evendeep/blob/master/diff/diff.go#L65).
   560  
   561  ### deepequal
   562  
   563  Our `DeepEqual` is shortcut to `DeepDiff`:
   564  
   565  ```go
   566  equal := evendeep.DeepEqual([]int{3, 0, 9}, []int{9, 3, 0}, diff.WithSliceOrderedComparison(true))
   567  if !equal {
   568    t.Errorf("expecting equal = true but got false")
   569  }
   570  ```
   571  
   572  For the unhandled types and objects, DeepEqual and DeepDiff will fallback to `reflect.DeepEqual()`. It's no need to
   573  call `reflect.DeepEqual` explicitly.
   574  
   575  ## Roadmap
   576  
   577  These features had been planning but still on ice.
   578  
   579  - [x] Name converting and mapping for `cms.ByOrdinal` (`*`) mode: a universal `name converter` can be applied in copying
   580    field to field.
   581  - [ ] *Use SourceExtractor and TargetSetter together (might be impossible)*
   582  - [ ] More builtin converters (*might not be a requisite*)
   583  - [x] Handle circular pointer (DONE)
   584  
   585  Issue me if you wanna put it or them on the table.
   586  
   587  ## LICENSE
   588  
   589  Under Apache 2.0.