-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
gen/enum: Add go.label
to override item name
#363
Changes from 2 commits
62e78a7
c34fbf3
50280ea
adcc38e
50a01b6
a77008f
dd737c3
906ce19
14241fc
dcefe66
ba9c0a0
7d43886
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,11 @@ import ( | |
"go.uber.org/thriftrw/compile" | ||
) | ||
|
||
const ( | ||
// GoLabel overrides name on the wire | ||
GoLabel = "go.label" | ||
) | ||
|
||
// enumGenerator generates code to serialize and deserialize enums. | ||
type enumGenerator struct{} | ||
|
||
|
@@ -98,7 +103,7 @@ func enum(g Generator, spec *compile.EnumSpec) error { | |
switch string(value) { | ||
<- $enum := .Spec -> | ||
<range .Spec.Items -> | ||
case "<.Name>": | ||
case "<enumItemWireName .>": | ||
*<$v> = <enumItemName $enumName .> | ||
return nil | ||
<end -> | ||
|
@@ -118,7 +123,7 @@ func enum(g Generator, spec *compile.EnumSpec) error { | |
switch int32(<$v>) { | ||
<range .UniqueItems -> | ||
case <.Value>: | ||
return []byte("<.Name>"), nil | ||
return []byte("<enumItemWireName .>"), nil | ||
<end -> | ||
} | ||
<end -> | ||
|
@@ -165,7 +170,7 @@ func enum(g Generator, spec *compile.EnumSpec) error { | |
switch <$w> { | ||
<range .UniqueItems -> | ||
case <.Value>: | ||
return "<.Name>" | ||
return "<enumItemWireName .>" | ||
<end -> | ||
} | ||
<end -> | ||
|
@@ -190,7 +195,7 @@ func enum(g Generator, spec *compile.EnumSpec) error { | |
switch int32(<$v>) { | ||
<range .UniqueItems -> | ||
case <.Value>: | ||
return ([]byte)("\"<.Name>\""), nil | ||
return ([]byte)("\"<enumItemWireName .>\""), nil | ||
<end -> | ||
} | ||
<end -> | ||
|
@@ -246,6 +251,7 @@ func enum(g Generator, spec *compile.EnumSpec) error { | |
UniqueItems: items, | ||
}, | ||
TemplateFunc("enumItemName", enumItemName), | ||
TemplateFunc("enumItemWireName", enumItemWireName), | ||
) | ||
|
||
return wrapGenerateError(spec.Name, err) | ||
|
@@ -265,6 +271,17 @@ func enumItemName(enumName string, spec *compile.EnumItem) (string, error) { | |
return enumName + name, err | ||
} | ||
|
||
// enumItemWireName returns the actual name used for serialization/deserialization | ||
// default to EnumItem.Name, override by the value of GoLabel | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit:
(I'm trying to make it clear that the label actually has nothing to do with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed, the serialization/deserialization is pretty misleading given ThriftRW's context here |
||
func enumItemWireName(spec *compile.EnumItem) string { | ||
labelName := spec.Name | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. optional: can be simplified: if val := spec.Annotations[GoLabel]; len(val) > 0 {
return val
}
return spec.Name |
||
val, ok := spec.Annotations[GoLabel] | ||
if ok && len(val) > 0 { | ||
labelName = val | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: If you use the single assignment form for a map, it returns the zero So this can be simplified to, if val := spec.Annotations[GoLabel]; len(val) > 0 {
labelName = val
} |
||
} | ||
return labelName | ||
} | ||
|
||
// enumUniqueItems returns a subset of the given list of enum items where | ||
// there are no value collisions between items. | ||
func enumUniqueItems(items []compile.EnumItem) []compile.EnumItem { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -399,3 +399,69 @@ func TestEnumAccessors(t *testing.T) { | |
}) | ||
}) | ||
} | ||
|
||
func TestEnumLabelLegit(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: s/Legit/Valid |
||
tests := []struct { | ||
enumItem te.EnumWithLabel | ||
wireValue string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: jsonValue not wireValue |
||
}{ | ||
{te.EnumWithLabelUsername, "\"surname\""}, | ||
{te.EnumWithLabelPassword, "\"hashed_password\""}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: can use backticks to avoid having to escape "
|
||
{te.EnumWithLabelSalt, "\"salt\""}, | ||
{te.EnumWithLabelSugar, "\"sugar\""}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add a case for unknown enums here. (They're still valid.)
|
||
} | ||
for _, tt := range tests { | ||
t.Run("compare label:"+tt.wireValue, func(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would just do |
||
// marshal | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added marshal / unmarshal behavior There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we put the JSON behavior check in a subtest since this is also testing roundtripping.
|
||
label := te.EnumWithLabel(tt.enumItem) | ||
b, err := json.Marshal(label) | ||
assert.NoError(t, err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should do require.NoError here so that the test doesn't proceed if it failed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤦♂️ |
||
assert.Equal(t, tt.wireValue, string(b)) | ||
|
||
// unmarshal | ||
var expectedLabel te.EnumWithLabel | ||
err = json.Unmarshal(b, &expectedLabel) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. require.NoError |
||
assert.Equal(t, expectedLabel, label) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With assert.Equal, the call pattern is
(We should fix naming.) |
||
|
||
assertRoundTrip( | ||
t, &tt.enumItem, | ||
wire.NewValueI32(int32(tt.enumItem)), | ||
"test roundtrip "+tt.wireValue, | ||
) | ||
}) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we also confirm through an integration test that this effects the desired behavior for JSON marshaling? Can we also test the behavior of serializing an unrecognized enum case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just want to make sure I understand your comment correctly, are you suggesting something here like: ...
// marshal
label := te.EnumWithWireLabel(te.EnumWithWireLabelUsername)
b, err := json.Marshal(label)
assert.Equal(t, "username", string(b))
// unmarlshal
var testLabel te.EnumWithWireLabel
err = json.Unmarshal(b, testLabel)
assert.Equal(t, testLabel, label)
... or you're suggesting a specific kind of integration tests in this repo? Thanks There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is fine. |
||
} | ||
|
||
func TestEnumLabelInvalid(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added test cases for invalid enum item marshal / unmarshal |
||
|
||
for _, tt := range []struct { | ||
value te.EnumWithLabel | ||
rep string | ||
}{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: for table tests, we prefer the following form.
Inlining the test cases with the range statement hurts readability IMO. |
||
{-1, "-1"}, | ||
{1 << 10, "1024"}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't technically invalid. These are valid enum items. They're just unknown. You can fold these cases into the valid case above. |
||
} { | ||
invalidEnumItem := te.EnumWithLabel(tt.value) | ||
b, err := json.Marshal(invalidEnumItem) | ||
assert.NoError(t, err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is default marshal behavior: if not matching any key, just marshal as string representation of int value, without raising error |
||
assert.Equal(t, tt.rep, string(b)) | ||
} | ||
|
||
for _, tt := range []struct { | ||
errVal []byte | ||
errMsg string | ||
}{ | ||
{ | ||
[]byte("some-random-str"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this test isn't testing the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/thriftrw/thriftrw-go/blob/dev/gen/internal/tests/enums/types.go#L98 I was testing against There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue is that this code never hits Using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, good catch! Thanks for this comment, I'll fix the test cases here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually looked at it again I added that first test case intentionally, you are right it didn't hit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should have a test for the original enum name, and ensure that it's no longer recognized. e.g., |
||
"invalid character 's' looking for beginning of value", | ||
}, | ||
{ | ||
[]byte("\"; drop table users;\""), | ||
"unknown enum value \"; drop table users;\" for \"EnumWithLabel\"", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: use backticks to avoid quoting " |
||
}, | ||
} { | ||
var expectedLabel te.EnumWithLabel | ||
err := json.Unmarshal(tt.errVal, &expectedLabel) | ||
assert.Equal(t, err.Error(), tt.errMsg) | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,5 +52,13 @@ enum lowerCaseEnum { | |
containing, lower_case, items | ||
} | ||
|
||
// EnumWithLabel use label name in serialization/deserialization | ||
enum EnumWithLabel { | ||
username (go.label = "surname"), | ||
password (go.label = "hashed_password"), | ||
salt (go.label = ""), | ||
sugar (go.label) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can the items be upper-case (the labels can be lower-case)? That matches our
And we should have one case where the label is a Thrift keyword since that's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good advice, enum name and its label is actually case sensitive, I added upper/lower cases for item name and label, also added one reserved keyword label |
||
} | ||
|
||
// collision with RecordType_Values() function. | ||
enum RecordType_Values { FOO, BAR } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
styling nit: const group unnecessary if it's just one constant.