Skip to content

Commit c9e1cdc

Browse files
authored
fix(sidekick): rustify accessors for bindings (#2365)
The accessors may need to be escaped if they are a rust keyword, and may have undergone a style transformation.
1 parent 860a976 commit c9e1cdc

File tree

2 files changed

+117
-4
lines changed

2 files changed

+117
-4
lines changed

internal/sidekick/internal/rust/annotate.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -794,14 +794,15 @@ func makeAccessors(fields []string, m *api.Method) []string {
794794
message := m.InputType
795795
for _, name := range fields {
796796
field := findField(name, message)
797+
rustFieldName := toSnake(name)
797798
if field == nil {
798-
slog.Error("invalid routing/path field for request message", "field", name, "message ID", message.ID)
799+
slog.Error("invalid routing/path field for request message", "field", rustFieldName, "message ID", message.ID)
799800
continue
800801
}
801802
if field.Optional {
802-
accessors = append(accessors, fmt.Sprintf(".and_then(|m| m.%s.as_ref())", name))
803+
accessors = append(accessors, fmt.Sprintf(".and_then(|m| m.%s.as_ref())", rustFieldName))
803804
} else {
804-
accessors = append(accessors, fmt.Sprintf(".map(|m| &m.%s)", name))
805+
accessors = append(accessors, fmt.Sprintf(".map(|m| &m.%s)", rustFieldName))
805806
}
806807
if field.Typez == api.STRING_TYPE {
807808
accessors = append(accessors, ".map(|s| s.as_str())")
@@ -859,9 +860,13 @@ func makeBindingSubstitution(v *api.PathVariable, m *api.Method) bindingSubstitu
859860
for _, a := range makeAccessors(v.FieldPath, m) {
860861
fieldAccessor += a
861862
}
863+
var rustNames []string
864+
for _, n := range v.FieldPath {
865+
rustNames = append(rustNames, toSnake(n))
866+
}
862867
return bindingSubstitution{
863868
FieldAccessor: fieldAccessor,
864-
FieldName: strings.Join(v.FieldPath, "."),
869+
FieldName: strings.Join(rustNames, "."),
865870
Template: v.Segments,
866871
}
867872
}

internal/sidekick/internal/rust/annotate_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1638,6 +1638,114 @@ func TestPathBindingAnnotations(t *testing.T) {
16381638
}
16391639
}
16401640

1641+
func TestPathBindingAnnotationsStyle(t *testing.T) {
1642+
for _, test := range []struct {
1643+
FieldName string
1644+
WantFieldName string
1645+
WantAccessor string
1646+
}{
1647+
{"machine", "machine", "Some(&req).map(|m| &m.machine).map(|s| s.as_str())"},
1648+
{"machineType", "machine_type", "Some(&req).map(|m| &m.machine_type).map(|s| s.as_str())"},
1649+
{"machine_type", "machine_type", "Some(&req).map(|m| &m.machine_type).map(|s| s.as_str())"},
1650+
{"type", "r#type", "Some(&req).map(|m| &m.r#type).map(|s| s.as_str())"},
1651+
} {
1652+
field := &api.Field{
1653+
Name: test.FieldName,
1654+
JSONName: test.FieldName,
1655+
ID: fmt.Sprintf(".test.Request.%s", test.FieldName),
1656+
Typez: api.STRING_TYPE,
1657+
}
1658+
request := &api.Message{
1659+
Name: "Request",
1660+
Package: "test",
1661+
ID: ".test.Request",
1662+
Fields: []*api.Field{field},
1663+
}
1664+
response := &api.Message{
1665+
Name: "Response",
1666+
Package: "test",
1667+
ID: ".test.Response",
1668+
}
1669+
binding := &api.PathBinding{
1670+
Verb: "GET",
1671+
PathTemplate: api.NewPathTemplate().
1672+
WithLiteral("v1").
1673+
WithLiteral("machines").
1674+
WithVariable(api.NewPathVariable(test.FieldName).
1675+
WithMatch()).
1676+
WithVerb("create"),
1677+
QueryParameters: map[string]bool{},
1678+
}
1679+
wantBinding := &pathBindingAnnotation{
1680+
PathFmt: "/v1/machines/{}:create",
1681+
Substitutions: []*bindingSubstitution{
1682+
{
1683+
FieldAccessor: test.WantAccessor,
1684+
FieldName: test.WantFieldName,
1685+
Template: []string{"*"},
1686+
},
1687+
},
1688+
}
1689+
method := &api.Method{
1690+
Name: "Create",
1691+
ID: ".test.Service.Create",
1692+
InputType: request,
1693+
InputTypeID: ".test.Request",
1694+
OutputTypeID: ".test.Response",
1695+
PathInfo: &api.PathInfo{
1696+
Bindings: []*api.PathBinding{binding},
1697+
},
1698+
}
1699+
service := &api.Service{
1700+
Name: "Service",
1701+
ID: ".test.Service",
1702+
Package: "test",
1703+
Methods: []*api.Method{method},
1704+
}
1705+
model := api.NewTestAPI(
1706+
[]*api.Message{request, response},
1707+
[]*api.Enum{},
1708+
[]*api.Service{service})
1709+
api.CrossReference(model)
1710+
codec, err := newCodec(true, map[string]string{})
1711+
if err != nil {
1712+
t.Fatal(err)
1713+
}
1714+
annotateModel(model, codec)
1715+
if diff := cmp.Diff(wantBinding, binding.Codec); diff != "" {
1716+
t.Errorf("mismatch in path binding annotations (-want, +got)\n:%s", diff)
1717+
}
1718+
1719+
}
1720+
}
1721+
1722+
func TestPathBindingAnnotationsErrors(t *testing.T) {
1723+
field := &api.Field{
1724+
Name: "field",
1725+
JSONName: "field",
1726+
ID: ".test.Request.field",
1727+
Typez: api.STRING_TYPE,
1728+
}
1729+
request := &api.Message{
1730+
Name: "Request",
1731+
Package: "test",
1732+
ID: ".test.Request",
1733+
Fields: []*api.Field{field},
1734+
}
1735+
method := &api.Method{
1736+
Name: "Create",
1737+
ID: ".test.Service.Create",
1738+
InputType: request,
1739+
InputTypeID: ".test.Request",
1740+
OutputTypeID: ".test.Response",
1741+
}
1742+
got := makeAccessors([]string{"not-a-field-name"}, method)
1743+
var want []string
1744+
if diff := cmp.Diff(want, got); diff != "" {
1745+
t.Errorf("mismatch (-want, +got):\n%s", diff)
1746+
}
1747+
}
1748+
16411749
func TestPathTemplateGeneration(t *testing.T) {
16421750
tests := []struct {
16431751
name string

0 commit comments

Comments
 (0)