Skip to content

Commit

Permalink
Trying to serialize transformers with @jba's idea
Browse files Browse the repository at this point in the history
  • Loading branch information
dsnet committed Jul 27, 2017
1 parent bf72641 commit 7523d91
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 11 deletions.
11 changes: 10 additions & 1 deletion cmp/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ var nothing = reflect.Value{}
// and the overall result is false.
//
// • Let S be the set of all Ignore, Transformer, and Comparer options that
// remain after applying all path filters, value filters, and type filters.
// remain after applying all implicit and explicit filters.
// If at least one Ignore exists in S, then the comparison is ignored.
// If the number of Transformer and Comparer options in S is greater than one,
// then Equal panics because it is ambiguous which option to use.
Expand Down Expand Up @@ -347,6 +347,15 @@ func (s *state) applyFilters(vx, vy reflect.Value, t reflect.Type, opt option) b
return false
}
}
if op, ok := opt.op.(*transformer); ok {
for i := len(s.curPath) - 1; i >= 0; i-- {
if t, ok := s.curPath[i].(*transform); ok && op == t.trans {
return false
} else {
break // Hit most recent non-Transform step
}
}
}
return true
}

Expand Down
9 changes: 2 additions & 7 deletions cmp/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,16 +258,11 @@ func ExampleOption_equalEmpty() {
// This example is for demonstrative purposes; use cmpopts.SortSlices instead.
func ExampleOption_sortedSlice() {
// This Transformer sorts a []int.
// Since the transformer transforms []int into []int, there is problem where
// this is recursively applied forever. To prevent this, use a FilterValues
// to first check for the condition upon which the transformer ought to apply.
trans := cmp.FilterValues(func(x, y []int) bool {
return !sort.IntsAreSorted(x) || !sort.IntsAreSorted(y)
}, cmp.Transformer("Sort", func(in []int) []int {
trans := cmp.Transformer("Sort", func(in []int) []int {
out := append([]int(nil), in...) // Copy input to avoid mutating it
sort.Ints(out)
return out
}))
})

x := struct{ Ints []int }{[]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}
y := struct{ Ints []int }{[]int{2, 8, 0, 9, 6, 1, 4, 7, 3, 5}}
Expand Down
14 changes: 11 additions & 3 deletions cmp/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,17 @@ func Ignore() Option {
// The transformer f must be a function "func(T) R" that converts values of
// type T to those of type R and is implicitly filtered to input values
// assignable to T. The transformer must not mutate T in any way.
// If T and R are the same type, an additional filter must be applied to
// act as the base case to prevent an infinite recursion applying the same
// transform to itself (see the SortedSlice example).
//
// To help prevent infinite cycles recursively applying the same transform to
// the output of itself (e.g., in the case where the input and output types
// are the same), an implicit filter is added such that a transformer is
// applicable only if that exact transformer is not already in the
// tail of the Path since the last non-Transform step.
// While this is sufficient for most usages of Transformers, this does not
// prevent all possible infinite cycles (such as in the case where the
// transformer returns an interface type, possibly appending a TypeAssertion
// step to the Path). In these cases, a filter must be added to act as the base
// case to prevent infinite recursion.
//
// The name is a user provided label that is used as the Transform.Name in the
// transformation PathStep. If empty, an arbitrary name is used.
Expand Down

0 comments on commit 7523d91

Please sign in to comment.