Skip to content

Commit

Permalink
Add Transform.Option helper method (#59)
Browse files Browse the repository at this point in the history
Currently there is no easy way to create non-reentrant transformers
where they do not recursively apply on their own output.
You could check for the Transformer name, but the API does not
require the names to be unique.

The only way to write a helper to do this is to generate some obscure
name that is guaranteed to probabilistic never have a conflict.

A better approach is to simply return the original Option
as returned by the Transformer constructor.
Thus, users can rely on == to check if a Transform step exactly
matches a Transformer that they created.

It would be useful to a non-reentrant transformer to cmpopts,
but that can be a future addition. It will also need a better name.
  • Loading branch information
dsnet committed Dec 14, 2017
1 parent 2c05626 commit 2809dbc
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 10 deletions.
50 changes: 40 additions & 10 deletions cmp/compare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -457,10 +457,25 @@ root:
}

func transformerTests() []test {
type JSON string
type StringBytes struct {
String string
Bytes []byte
}

const label = "Transformer"

transformOnce := func(name string, f interface{}) cmp.Option {
xform := cmp.Transformer(name, f)
return cmp.FilterPath(func(p cmp.Path) bool {
for _, ps := range p {
if tr, ok := ps.(cmp.Transform); ok && tr.Option() == xform {
return false
}
}
return true
}, xform)
}

return []test{{
label: label,
x: uint8(0),
Expand Down Expand Up @@ -522,7 +537,7 @@ func transformerTests() []test {
+: 1`,
}, {
label: label,
x: JSON(`{
x: `{
"firstName": "John",
"lastName": "Smith",
"age": 25,
Expand All @@ -544,33 +559,48 @@ func transformerTests() []test {
"type": "mobile"
}],
"children": []
}`),
y: JSON(`{"firstName":"John","lastName":"Smith","isAlive":true,"age":25,
}`,
y: `{"firstName":"John","lastName":"Smith","isAlive":true,"age":25,
"address":{"streetAddress":"21 2nd Street","city":"New York",
"state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home",
"number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{
"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`),
"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`,
opts: []cmp.Option{
cmp.Transformer("ParseJSON", func(s JSON) (m map[string]interface{}) {
transformOnce("ParseJSON", func(s string) (m map[string]interface{}) {
if err := json.Unmarshal([]byte(s), &m); err != nil {
panic(err)
}
return m
}),
},
wantDiff: `
ParseJSON({cmp_test.JSON})["address"]["city"]:
ParseJSON({string})["address"]["city"]:
-: "Los Angeles"
+: "New York"
ParseJSON({cmp_test.JSON})["address"]["state"]:
ParseJSON({string})["address"]["state"]:
-: "CA"
+: "NY"
ParseJSON({cmp_test.JSON})["phoneNumbers"][0]["number"]:
ParseJSON({string})["phoneNumbers"][0]["number"]:
-: "212 555-4321"
+: "212 555-1234"
ParseJSON({cmp_test.JSON})["spouse"]:
ParseJSON({string})["spouse"]:
-: <non-existent>
+: interface {}(nil)`,
}, {
label: label,
x: StringBytes{String: "some\nmulti\nLine\nstring", Bytes: []byte("some\nmulti\nline\nbytes")},
y: StringBytes{String: "some\nmulti\nline\nstring", Bytes: []byte("some\nmulti\nline\nBytes")},
opts: []cmp.Option{
transformOnce("SplitString", func(s string) []string { return strings.Split(s, "\n") }),
transformOnce("SplitBytes", func(b []byte) [][]byte { return bytes.Split(b, []byte("\n")) }),
},
wantDiff: `
SplitString({cmp_test.StringBytes}.String)[2]:
-: "Line"
+: "line"
SplitBytes({cmp_test.StringBytes}.Bytes)[3][0]:
-: 0x62
+: 0x42`,
}}
}

Expand Down
6 changes: 6 additions & 0 deletions cmp/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ type (
PathStep
Name() string
Func() reflect.Value

// Option returns the originally constructed Transformer option.
// The == operator can be used to detect the exact option used.
Option() Option

isTransform()
}
)
Expand Down Expand Up @@ -260,6 +265,7 @@ func (sf structField) Name() string { return sf.name }
func (sf structField) Index() int { return sf.idx }
func (tf transform) Name() string { return tf.trans.name }
func (tf transform) Func() reflect.Value { return tf.trans.fnc }
func (tf transform) Option() Option { return tf.trans }

func (pathStep) isPathStep() {}
func (sliceIndex) isSliceIndex() {}
Expand Down

0 comments on commit 2809dbc

Please sign in to comment.