Skip to content

Commit

Permalink
Merge pull request #12 from nicheinc/feature/apply-ptr
Browse files Browse the repository at this point in the history
Add ApplyPtr and DiffPtr methods to Update[T]
  • Loading branch information
jonathansharman authored Aug 25, 2022
2 parents 20c4b70 + c7e0404 commit b70bdfc
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 5 deletions.
4 changes: 2 additions & 2 deletions nup/sliceUpdate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestSliceUpdate_UnmarshalJSON(t *testing.T) {
if err := json.Unmarshal([]byte(testCase.json), &dst); err != nil {
t.Errorf("Error unmarshaling JSON: %s", err)
}
if !reflect.DeepEqual(dst.Update, testCase.expected) {
if !dst.Update.Equal(testCase.expected) {
t.Errorf("Expected: %v. Actual: %v", testCase.expected, dst.Update)
}
})
Expand Down Expand Up @@ -84,7 +84,7 @@ func TestSliceRemoveOrSet(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
actual := SliceRemoveOrSet(testCase.value)
if !reflect.DeepEqual(actual, testCase.expected) {
if !actual.Equal(testCase.expected) {
t.Errorf("Expected: %v. Actual: %v", testCase.expected, actual)
}
})
Expand Down
32 changes: 32 additions & 0 deletions nup/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,22 @@ func (u Update[T]) Apply(value T) T {
}
}

// ApplyPtr returns the result of applying the update to the given pointer
// value. The result is the given value if the update is a no-op, nil if it's a
// removal, or a copy of the update's contained value if it's a set operation.
func (u Update[T]) ApplyPtr(value *T) *T {
switch u.op {
case OpNoop:
return value
case OpRemove:
return nil
default: // Set
// Copy the update value so it can't be mutated via the returned pointer.
value := u.value
return &value
}
}

// Diff returns the update itself if Apply(value) != value; otherwise it returns
// a no-op update. Diff can be used to omit extraneous updates when applying
// them would have no effect.
Expand All @@ -126,6 +142,22 @@ func (u Update[T]) Diff(value T) Update[T] {
return u
}

// DiffPtr returns the update itself if ApplyPtr(value) does not contain a value
// equal to the given value; otherwise it returns a no-op update. DiffPtr can be
// used to omit extraneous updates when applying them would have no effect.
func (u Update[T]) DiffPtr(value *T) Update[T] {
applied := u.ApplyPtr(value)
switch {
case applied == nil && value == nil:
return Noop[T]()
case applied == nil || value == nil:
return u
default:
return u.Diff(*value)
}

}

// UnmarshalJSON implements json.Unmarshaler.
func (u *Update[T]) UnmarshalJSON(data []byte) error {
if string(data) == "null" {
Expand Down
101 changes: 98 additions & 3 deletions nup/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestUpdate_UnmarshalJSON(t *testing.T) {
if err := json.Unmarshal([]byte(testCase.json), &dst); err != nil {
t.Errorf("Error unmarshaling JSON: %s", err)
}
if !reflect.DeepEqual(dst.Update, testCase.expected) {
if dst.Update != testCase.expected {
t.Errorf("Expected: %v. Actual: %v", testCase.expected, dst.Update)
}
})
Expand Down Expand Up @@ -293,6 +293,42 @@ func TestUpdate_Apply(t *testing.T) {
}
}

func TestUpdate_ApplyPtr(t *testing.T) {
var (
value1 = 1
value2 = 2
)
testCases := []struct {
name string
update Update[int]
expected *int
}{
{
name: "Noop",
update: Noop[int](),
expected: &value1,
},
{
name: "Remove",
update: Remove[int](),
expected: nil,
},
{
name: "Set",
update: Set(value2),
expected: &value2,
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
if actual := testCase.update.ApplyPtr(&value1); !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("Expected: %v. Actual: %v", testCase.expected, actual)
}
})
}
}

func TestUpdate_Diff(t *testing.T) {
var (
value1 = 1
Expand All @@ -319,7 +355,7 @@ func TestUpdate_Diff(t *testing.T) {
{
name: "Remove/ZeroValue",
update: Remove[int](),
value: 0.0,
value: 0,
expected: Noop[int](),
},
{
Expand All @@ -338,7 +374,66 @@ func TestUpdate_Diff(t *testing.T) {

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
if actual := testCase.update.Diff(testCase.value); !reflect.DeepEqual(actual, testCase.expected) {
if actual := testCase.update.Diff(testCase.value); actual != testCase.expected {
t.Errorf("Expected: %v. Actual: %v", testCase.expected, actual)
}
})
}
}

func TestUpdate_DiffPtr(t *testing.T) {
var (
zero = 0
value1 = 1
value2 = 2
)
testCases := []struct {
name string
update Update[int]
value *int
expected Update[int]
}{
{
name: "Noop",
update: Noop[int](),
value: &value1,
expected: Noop[int](),
},
{
name: "Remove/ZeroValue",
update: Remove[int](),
value: &zero,
expected: Remove[int](),
},
{
name: "Remove/NonZeroValue",
update: Remove[int](),
value: &value1,
expected: Remove[int](),
},
{
name: "Remove/Nil",
update: Remove[int](),
value: nil,
expected: Noop[int](),
},
{
name: "Set/Equal",
update: Set(value1),
value: &value1,
expected: Noop[int](),
},
{
name: "Set/NotEqual",
update: Set(value2),
value: &value1,
expected: Set(value2),
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
if actual := testCase.update.DiffPtr(testCase.value); actual != testCase.expected {
t.Errorf("Expected: %v. Actual: %v", testCase.expected, actual)
}
})
Expand Down

0 comments on commit b70bdfc

Please sign in to comment.