Documentation ¶
Index ¶
- Constants
- Variables
- func AllowTypeMismatch(enabled bool) func(d *Differ) error
- func AreType(a, b reflect.Value, types ...reflect.Type) bool
- func Changed(a, b interface{}) bool
- func ConvertCompatibleTypes() func(d *Differ) error
- func CustomValueDiffers(vd ...ValueDiffer) func(d *Differ) error
- func DisableStructValues() func(d *Differ) error
- func DiscardComplexOrigin() func(d *Differ) error
- func Filter(f FilterFunc) func(d *Differ) error
- func FlattenEmbeddedStructs() func(d *Differ) error
- func SliceOrdering(enabled bool) func(d *Differ) error
- func StructMapKeySupport() func(d *Differ) error
- func TagName(tag string) func(d *Differ) error
- type Change
- type ChangeValue
- func (c *ChangeValue) AddError(err error) *ChangeValue
- func (c *ChangeValue) ClearFlag(flag PatchFlags)
- func (c *ChangeValue) HasFlag(flag PatchFlags) bool
- func (c ChangeValue) Index(i int) reflect.Value
- func (c *ChangeValue) IsValid() bool
- func (c ChangeValue) Len() int
- func (c ChangeValue) NewArrayElement() reflect.Value
- func (c ChangeValue) NewElement() reflect.Value
- func (c ChangeValue) ParentIndex(i int) (ret reflect.Value)
- func (c ChangeValue) ParentKind() reflect.Kind
- func (c ChangeValue) ParentLen() (ret int)
- func (c *ChangeValue) ParentSet(value reflect.Value, convertCompatibleTypes bool)
- func (c *ChangeValue) Set(value reflect.Value, convertCompatibleTypes bool)
- func (c *ChangeValue) SetFlag(flag PatchFlags)
- type Changelog
- type Comparative
- type ComparativeList
- type DiffError
- type DiffFunc
- type DiffType
- type Differ
- type FilterFunc
- type PatchFlags
- type PatchLog
- type PatchLogEntry
- type ValueDiffer
Examples ¶
Constants ¶
const ( // CREATE represents when an element has been added CREATE = "create" // UPDATE represents when an element has been updated UPDATE = "update" // DELETE represents when an element has been removed DELETE = "delete" )
Variables ¶
var ( // ErrTypeMismatch Compared types do not match ErrTypeMismatch = NewError("types do not match") // ErrInvalidChangeType The specified change values are not unsupported ErrInvalidChangeType = NewError("change type must be one of 'create' or 'delete'") )
Functions ¶
func AllowTypeMismatch ¶
AllowTypeMismatch changed behaviour to report value as "updated" when its type has changed instead of error
func ConvertCompatibleTypes ¶
ConvertTypes enables values that are convertible to the target type to be converted when patching
func CustomValueDiffers ¶
func CustomValueDiffers(vd ...ValueDiffer) func(d *Differ) error
CustomValueDiffers allows you to register custom differs for specific types
func DisableStructValues ¶
DisableStructValues disables populating a separate change for each item in a struct, where the struct is being compared to a nil value
func DiscardComplexOrigin ¶
DiscardComplexOrigin - by default, we are now keeping the complex struct associated with a create entry. This allows us to fix the merge to new object issue of not having enough change log details when allocating new objects. This however is a trade off of memory size and complexity vs correctness which is often only necessary when embedding structs in slices and arrays. It memory constrained environments, it may be desirable to turn this feature off however from a computational perspective, keeping the complex origin is actually quite cheap so, make sure you're extremely clear on the pitfalls of turning this off prior to doing so.
func Filter ¶
func Filter(f FilterFunc) func(d *Differ) error
Filter allows you to determine which fields the differ descends into
Example ¶
type Tag struct { Name string `diff:"name,identifier"` Value string `diff:"value"` } type Fruit struct { ID int `diff:"id"` Name string `diff:"name"` Healthy bool `diff:"healthy"` Nutrients []string `diff:"nutrients"` Tags []Tag `diff:"tags"` } a := Fruit{ ID: 1, Name: "Green Apple", Healthy: true, Nutrients: []string{ "vitamin c", "vitamin d", }, } b := Fruit{ ID: 2, Name: "Red Apple", Healthy: true, Nutrients: []string{ "vitamin c", "vitamin d", "vitamin e", }, } d, err := diff.NewDiffer(diff.Filter(func(path []string, parent reflect.Type, field reflect.StructField) bool { return field.Name != "Name" })) if err != nil { panic(err) } changelog, err := d.Diff(a, b) if err != nil { panic(err) } fmt.Printf("%#v", changelog)
Output: diff.Changelog{diff.Change{Type:"update", Path:[]string{"id"}, From:1, To:2, parent:diff_test.Fruit{ID:1, Name:"Green Apple", Healthy:true, Nutrients:[]string{"vitamin c", "vitamin d"}, Tags:[]diff_test.Tag(nil)}}, diff.Change{Type:"create", Path:[]string{"nutrients", "2"}, From:interface {}(nil), To:"vitamin e", parent:interface {}(nil)}}
func FlattenEmbeddedStructs ¶
FlattenEmbeddedStructs determines whether fields of embedded structs should behave as if they are directly under the parent
func SliceOrdering ¶
SliceOrdering determines whether the ordering of items in a slice results in a change
func StructMapKeySupport ¶
StructMapKeySupport - Changelog paths do not provided structured object values for maps that contain complex keys (such as other structs). You must enable this support via an option and it then uses msgpack to encode path elements that are structs. If you don't have this on, and try to patch, your apply will fail for that element.
Types ¶
type Change ¶
type Change struct { Type string `json:"type"` Path []string `json:"path"` From interface{} `json:"from"` To interface{} `json:"to"` // contains filtered or unexported fields }
Change stores information about a changed item
type ChangeValue ¶
type ChangeValue struct {
// contains filtered or unexported fields
}
ChangeValue is a specialized struct for monitoring patching
func NewChangeValue ¶
func NewChangeValue(d *Differ, c Change, target interface{}) (ret *ChangeValue)
NewChangeValue idiomatic constructor (also invokes render)
func (*ChangeValue) AddError ¶
func (c *ChangeValue) AddError(err error) *ChangeValue
AddError appends errors to this change value
func (*ChangeValue) ClearFlag ¶
func (c *ChangeValue) ClearFlag(flag PatchFlags)
ClearFlag removes just a single flag
func (*ChangeValue) HasFlag ¶
func (c *ChangeValue) HasFlag(flag PatchFlags) bool
HasFlag indicates if a flag is set on the node. returns false if node is bad
func (ChangeValue) NewArrayElement ¶
func (c ChangeValue) NewArrayElement() reflect.Value
NewArrayElement gives us a dynamically typed new element
func (ChangeValue) NewElement ¶
func (c ChangeValue) NewElement() reflect.Value
Instance a new element of type for target. Taking the copy of the complex origin avoids the 'lack of data' issue present when allocating complex structs with slices and arrays
func (ChangeValue) ParentIndex ¶
func (c ChangeValue) ParentIndex(i int) (ret reflect.Value)
ParentIndex - get us the parent version, nil safe
func (ChangeValue) ParentKind ¶
func (c ChangeValue) ParentKind() reflect.Kind
ParentKind - helps keep us nil safe
func (ChangeValue) ParentLen ¶
func (c ChangeValue) ParentLen() (ret int)
ParentLen is a nil safe parent length check
func (*ChangeValue) ParentSet ¶
func (c *ChangeValue) ParentSet(value reflect.Value, convertCompatibleTypes bool)
ParentSet - nil safe parent set
func (*ChangeValue) Set ¶
func (c *ChangeValue) Set(value reflect.Value, convertCompatibleTypes bool)
Set echos reflect set
func (*ChangeValue) SetFlag ¶
func (c *ChangeValue) SetFlag(flag PatchFlags)
Sets a flag on the node and saves the change
type Changelog ¶
type Changelog []Change
Changelog stores a list of changed items
func Diff ¶
Diff returns a changelog of all mutated values from both
Example ¶
type Tag struct { Name string `diff:"name,identifier"` Value string `diff:"value"` } type Fruit struct { ID int `diff:"id"` Name string `diff:"name"` Healthy bool `diff:"healthy"` Nutrients []string `diff:"nutrients"` Tags []Tag `diff:"tags"` } a := Fruit{ ID: 1, Name: "Green Apple", Healthy: true, Nutrients: []string{ "vitamin c", "vitamin d", }, Tags: []Tag{ { Name: "kind", Value: "fruit", }, }, } b := Fruit{ ID: 2, Name: "Red Apple", Healthy: true, Nutrients: []string{ "vitamin c", "vitamin d", "vitamin e", }, Tags: []Tag{ { Name: "popularity", Value: "high", }, { Name: "kind", Value: "fruit", }, }, } changelog, err := diff.Diff(a, b) if err != nil { panic(err) } fmt.Printf("%#v", changelog) // Produces: diff.Changelog{diff.Change{Type:"update", Path:[]string{"id"}, From:1, To:2}, diff.Change{Type:"update", Path:[]string{"name"}, From:"Green Apple", To:"Red Apple"}, diff.Change{Type:"create", Path:[]string{"nutrients", "2"}, From:interface {}(nil), To:"vitamin e"}, diff.Change{Type:"create", Path:[]string{"tags", "popularity"}, From:interface {}(nil), To:main.Tag{Name:"popularity", Value:"high"}}}
Output:
func StructValues ¶
StructValues gets all values from a struct values are stored as "created" or "deleted" entries in the changelog, depending on the change type specified
type ComparativeList ¶
type ComparativeList struct {
// contains filtered or unexported fields
}
ComparativeList : stores indexed comparative
func NewComparativeList ¶
func NewComparativeList() *ComparativeList
NewComparativeList : returns a new comparative list
type DiffError ¶
type DiffError struct {
// contains filtered or unexported fields
}
our own version of an error, which can wrap others
type Differ ¶
type Differ struct { TagName string SliceOrdering bool DisableStructValues bool AllowTypeMismatch bool DiscardParent bool StructMapKeys bool FlattenEmbeddedStructs bool ConvertCompatibleTypes bool Filter FilterFunc // contains filtered or unexported fields }
Differ a configurable diff instance
type FilterFunc ¶
FilterFunc is a function that determines whether to descend into a struct field. parent is the struct being examined and field is a field on that struct. path is the path to the field from the root of the diff.
type PatchFlags ¶
type PatchFlags uint32
Not strictly necessary but might be nice in some cases
const ( OptionCreate PatchFlags = 1 << iota OptionNoCreate OptionOmitUnequal OptionImmutable FlagInvalidTarget FlagApplied FlagFailed FlagCreated FlagIgnored FlagDeleted FlagUpdated FlagParentSetApplied FlagParentSetFailed )
type PatchLog ¶
type PatchLog []PatchLogEntry
func Merge ¶
Example ¶
ExampleMerge demonstrates how to use the Merge function
type Fruit struct { ID int `diff:"ID" json:"Identifier"` Name string `diff:"name"` Healthy bool `diff:"healthy"` Nutrients []string `diff:"nutrients,create,omitunequal"` Labels map[string]int `diff:"labs,create"` } a := Fruit{ ID: 1, Name: "Green Apple", Healthy: true, Nutrients: []string{ "vitamin a", "vitamin b", "vitamin c", "vitamin d", }, Labels: make(map[string]int), } a.Labels["likes"] = 10 a.Labels["colors"] = 2 b := Fruit{ ID: 2, Name: "Red Apple", Healthy: true, Nutrients: []string{ "vitamin c", "vitamin d", "vitamin e", }, Labels: make(map[string]int), } b.Labels["forests"] = 1223 b.Labels["colors"] = 1222 c := Fruit{ Labels: make(map[string]int), Nutrients: []string{ "vitamin a", "vitamin c", "vitamin d", }, } c.Labels["likes"] = 21 c.Labels["colors"] = 42 //the only error that can happen here comes from the diff step patchLog, _ := diff.Merge(a, b, &c) //Note that unlike our patch version we've not included 'create' in the //tag for nutrients. This will omit "vitamin e" from ending up in c fmt.Printf("%#v", len(patchLog))
Output: 8
func Patch ¶
Example ¶
ExamplePatch demonstrates how to use the Patch function
type Key struct { value string weight int } type Cycle struct { Name string `diff:"name,create"` Count int `diff:"count,create"` } type Fruit struct { ID int `diff:"ID" json:"Identifier"` Name string `diff:"name"` Healthy bool `diff:"healthy"` Nutrients []string `diff:"nutrients,create,omitunequal"` Labels map[Key]Cycle `diff:"labs,create"` Cycles []Cycle `diff:"cycles,immutable"` Weights []int } a := Fruit{ ID: 1, Name: "Green Apple", Healthy: true, Nutrients: []string{ "vitamin a", "vitamin b", "vitamin c", "vitamin d", }, Labels: make(map[Key]Cycle), } a.Labels[Key{value: "likes"}] = Cycle{ Count: 10, } a.Labels[Key{value: "colors"}] = Cycle{ Count: 2, } b := Fruit{ ID: 2, Name: "Red Apple", Healthy: true, Nutrients: []string{ "vitamin c", "vitamin d", "vitamin e", }, Labels: make(map[Key]Cycle), Weights: []int{ 1, 2, 3, 4, }, } b.Labels[Key{value: "forests"}] = Cycle{ Count: 1223, } b.Labels[Key{value: "colors"}] = Cycle{ Count: 1222, } c := Fruit{ //Labels: make(map[string]int), Nutrients: []string{ "vitamin a", "vitamin c", "vitamin d", }, } //c.Labels["likes"] = 21 d := a d.Cycles = []Cycle{ Cycle{ Name: "First", Count: 45, }, Cycle{ Name: "Third", Count: 4, }, } d.Nutrients = append(d.Nutrients, "minerals") changelog, err := diff.Diff(a, b) if err != nil { panic(err) } patchLog := diff.Patch(changelog, &c) changelog, _ = diff.Diff(a, d) patchLog = diff.Patch(changelog, &c) fmt.Printf("%#v", len(patchLog))
Output: 1
func (PatchLog) Applied ¶
Applied - returns true if all change log entries were actually
applied, regardless of if any errors were encountered
func (PatchLog) ErrorCount ¶
ErrorCount -- counts the number of errors encountered while patching
type PatchLogEntry ¶
type PatchLogEntry struct { Path []string `json:"path"` From interface{} `json:"from"` To interface{} `json:"to"` Flags PatchFlags `json:"flags"` Errors error `json:"errors"` }
PatchLogEntry defines how a DiffLog entry was applied
func NewPatchLogEntry ¶
func NewPatchLogEntry(cv *ChangeValue) PatchLogEntry
NewPatchLogEntry converts our complicated reflection based struct to a simpler format for the consumer
func (PatchLogEntry) HasFlag ¶
func (p PatchLogEntry) HasFlag(flag PatchFlags) bool
HasFlag - convenience function for users
type ValueDiffer ¶
type ValueDiffer interface { Match(a, b reflect.Value) bool Diff(dt DiffType, df DiffFunc, cl *Changelog, path []string, a, b reflect.Value, parent interface{}) error InsertParentDiffer(dfunc func(path []string, a, b reflect.Value, p interface{}) error) }
ValueDiffer is an interface for custom differs