package diff import ( "fmt" "reflect" ) //ChangeValue is a specialized struct for monitoring patching type ChangeValue struct { parent *reflect.Value target *reflect.Value flags PatchFlags change *Change err error pos int index int key reflect.Value } //swap swaps out the target as we move down the path. Note that a nil // check is foregone here due to the fact we control usage. func (c *ChangeValue) swap(newTarget *reflect.Value) { if newTarget.IsValid() { c.ClearFlag(FlagInvalidTarget) c.parent = c.target c.target = newTarget c.pos++ } } // Sets a flag on the node and saves the change func (c *ChangeValue) SetFlag(flag PatchFlags) { if c != nil { c.flags = c.flags | flag } } //ClearFlag removes just a single flag func (c *ChangeValue) ClearFlag(flag PatchFlags) { if c != nil { c.flags = c.flags &^ flag } } //HasFlag indicates if a flag is set on the node. returns false if node is bad func (c *ChangeValue) HasFlag(flag PatchFlags) bool { return (c.flags & flag) != 0 } //IsValid echo for is valid func (c *ChangeValue) IsValid() bool { if c != nil { return c.target.IsValid() || !c.HasFlag(FlagInvalidTarget) } return false } //ParentKind - helps keep us nil safe func (c ChangeValue) ParentKind() reflect.Kind { if c.parent != nil { return c.parent.Kind() } return reflect.Invalid } //ParentLen is a nil safe parent length check func (c ChangeValue) ParentLen() (ret int) { if c.parent != nil && (c.parent.Kind() == reflect.Slice || c.parent.Kind() == reflect.Map) { ret = c.parent.Len() } return } //ParentSet - nil safe parent set func (c *ChangeValue) ParentSet(value reflect.Value, convertCompatibleTypes bool) { if c != nil && c.parent != nil { defer func() { if r := recover(); r != nil { c.SetFlag(FlagParentSetFailed) } }() if convertCompatibleTypes { if !value.Type().ConvertibleTo(c.parent.Type()) { c.AddError(fmt.Errorf("Value of type %s is not convertible to %s", value.Type().String(), c.parent.Type().String())) c.SetFlag(FlagParentSetFailed) return } c.parent.Set(value.Convert(c.parent.Type())) } else { c.parent.Set(value) } c.SetFlag(FlagParentSetApplied) } } //Len echo for len func (c ChangeValue) Len() int { return c.target.Len() } //Set echos reflect set func (c *ChangeValue) Set(value reflect.Value, convertCompatibleTypes bool) { if c == nil { return } defer func() { if r := recover(); r != nil { switch e := r.(type) { case string: c.AddError(NewError(e)) case *reflect.ValueError: c.AddError(NewError(e.Error())) } c.SetFlag(FlagFailed) } }() if c.HasFlag(OptionImmutable) { c.SetFlag(FlagIgnored) return } if convertCompatibleTypes { if c.target.Kind() == reflect.Ptr && value.Kind() != reflect.Ptr { if !value.IsValid() { c.target.Set(reflect.Zero(c.target.Type())) c.SetFlag(FlagApplied) return } else if !value.Type().ConvertibleTo(c.target.Elem().Type()) { c.AddError(fmt.Errorf("Value of type %s is not convertible to %s", value.Type().String(), c.target.Type().String())) c.SetFlag(FlagFailed) return } tv := reflect.New(c.target.Elem().Type()) tv.Elem().Set(value.Convert(c.target.Elem().Type())) c.target.Set(tv) } else { if !value.Type().ConvertibleTo(c.target.Type()) { c.AddError(fmt.Errorf("Value of type %s is not convertible to %s", value.Type().String(), c.target.Type().String())) c.SetFlag(FlagFailed) return } c.target.Set(value.Convert(c.target.Type())) } } else { if value.IsValid() { if c.target.Kind() == reflect.Ptr && value.Kind() != reflect.Ptr { tv := reflect.New(value.Type()) tv.Elem().Set(value) c.target.Set(tv) } else { c.target.Set(value) } } else if c.target.Kind() == reflect.Ptr { c.target.Set(reflect.Zero(c.target.Type())) } else if !c.target.IsZero() { t := c.target.Elem() t.Set(reflect.Zero(t.Type())) } } c.SetFlag(FlagApplied) } //Index echo for index func (c ChangeValue) Index(i int) reflect.Value { return c.target.Index(i) } //ParentIndex - get us the parent version, nil safe func (c ChangeValue) ParentIndex(i int) (ret reflect.Value) { if c.parent != nil { ret = c.parent.Index(i) } return } //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 (c ChangeValue) NewElement() reflect.Value { ret := c.change.parent if ret != nil { return reflect.ValueOf(ret) } return reflect.New(c.target.Type().Elem()).Elem() } //NewArrayElement gives us a dynamically typed new element func (c ChangeValue) NewArrayElement() reflect.Value { c.target.Set(reflect.Append(*c.target, c.NewElement())) c.SetFlag(FlagCreated) return c.Index(c.Len() - 1) } //AddError appends errors to this change value func (c *ChangeValue) AddError(err error) *ChangeValue { if c != nil { c.err = err } return c }