Skip to content

Commit eb7c26b

Browse files
authored
feat(sidekick/swift): generate empty enums (#5262)
For package-level enums this generates the correct file with the copyright boilerplate and an empty enum. A future PR will add the missing enum values.
1 parent e51b7de commit eb7c26b

6 files changed

Lines changed: 183 additions & 0 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package swift
16+
17+
import (
18+
"github.com/googleapis/librarian/internal/sidekick/api"
19+
)
20+
21+
type enumAnnotations struct {
22+
CopyrightYear string
23+
BoilerPlate []string
24+
Name string
25+
DocLines []string
26+
}
27+
28+
func (codec *codec) annotateEnum(enum *api.Enum, model *modelAnnotations) {
29+
docLines := codec.formatDocumentation(enum.Documentation)
30+
annotations := &enumAnnotations{
31+
CopyrightYear: model.CopyrightYear,
32+
BoilerPlate: model.BoilerPlate,
33+
Name: pascalCase(enum.Name),
34+
DocLines: docLines,
35+
}
36+
37+
enum.Codec = annotations
38+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package swift
16+
17+
import (
18+
"testing"
19+
20+
"github.com/google/go-cmp/cmp"
21+
"github.com/google/go-cmp/cmp/cmpopts"
22+
"github.com/googleapis/librarian/internal/sidekick/api"
23+
)
24+
25+
func TestAnnotateEnum(t *testing.T) {
26+
for _, test := range []struct {
27+
name string
28+
enumName string
29+
documentation string
30+
wantName string
31+
wantDocs []string
32+
}{
33+
{
34+
name: "basic enum",
35+
enumName: "Color",
36+
documentation: "A color enum.\nWith two lines.",
37+
wantName: "Color",
38+
wantDocs: []string{"A color enum.", "With two lines."},
39+
},
40+
{
41+
name: "escaped name",
42+
enumName: "Protocol",
43+
documentation: "An enum named Protocol.",
44+
wantName: "Protocol_",
45+
wantDocs: []string{"An enum named Protocol."},
46+
},
47+
} {
48+
t.Run(test.name, func(t *testing.T) {
49+
enum := &api.Enum{
50+
Name: test.enumName,
51+
Documentation: test.documentation,
52+
ID: ".test." + test.enumName,
53+
Package: "test",
54+
}
55+
model := api.NewTestAPI([]*api.Message{}, []*api.Enum{enum}, []*api.Service{})
56+
codec := newTestCodec(t, model, map[string]string{})
57+
if err := codec.annotateModel(); err != nil {
58+
t.Fatal(err)
59+
}
60+
want := &enumAnnotations{
61+
Name: test.wantName,
62+
DocLines: test.wantDocs,
63+
}
64+
65+
if diff := cmp.Diff(want, enum.Codec, cmpopts.IgnoreFields(enumAnnotations{}, "BoilerPlate", "CopyrightYear")); diff != "" {
66+
t.Errorf("mismatch (-want +got):\n%s", diff)
67+
}
68+
})
69+
}
70+
}

internal/sidekick/swift/annotate_model.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ func (codec *codec) annotateModel() error {
5959
return err
6060
}
6161
}
62+
for _, enum := range codec.Model.Enums {
63+
codec.annotateEnum(enum, annotations)
64+
}
6265
for _, service := range codec.Model.Services {
6366
codec.annotateService(service, annotations)
6467
}

internal/sidekick/swift/generate.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ func Generate(ctx context.Context, model *api.API, outdir string, cfg *parser.Mo
4848
if err := codec.generateMessages(outdir, model, provider); err != nil {
4949
return err
5050
}
51+
if err := codec.generateEnums(outdir, model, provider); err != nil {
52+
return err
53+
}
5154
if err := codec.generateServices(outdir, model, provider); err != nil {
5255
return err
5356
}
@@ -68,6 +71,19 @@ func (c *codec) generateMessages(outdir string, model *api.API, provider languag
6871
return nil
6972
}
7073

74+
func (c *codec) generateEnums(outdir string, model *api.API, provider language.TemplateProvider) error {
75+
for _, e := range model.Enums {
76+
generated := language.GeneratedFile{
77+
TemplatePath: "templates/common/enum.swift.mustache",
78+
OutputPath: filepath.Join("Sources", c.PackageName, e.Name+".swift"),
79+
}
80+
if err := language.GenerateEnum(outdir, e, provider, generated); err != nil {
81+
return err
82+
}
83+
}
84+
return nil
85+
}
86+
7187
func (c *codec) generateServices(outdir string, model *api.API, provider language.TemplateProvider) error {
7288
for _, s := range model.Services {
7389
generated := language.GeneratedFile{

internal/sidekick/swift/generate_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,31 @@ func TestGenerateServiceFiles(t *testing.T) {
127127
}
128128
}
129129
}
130+
131+
func TestGenerateEnumFiles(t *testing.T) {
132+
outDir := t.TempDir()
133+
134+
color := &api.Enum{Name: "Color", Package: "google.cloud.test.v1", ID: ".google.cloud.test.v1.Color"}
135+
kind := &api.Enum{Name: "Kind", Package: "google.cloud.test.v1", ID: ".google.cloud.test.v1.Kind"}
136+
137+
model := api.NewTestAPI([]*api.Message{}, []*api.Enum{color, kind}, []*api.Service{})
138+
model.PackageName = "google.cloud.test.v1"
139+
140+
cfg := &parser.ModelConfig{
141+
Codec: map[string]string{
142+
"copyright-year": "2038",
143+
},
144+
}
145+
146+
if err := Generate(t.Context(), model, outDir, cfg, nil); err != nil {
147+
t.Fatal(err)
148+
}
149+
150+
expectedDir := filepath.Join(outDir, "Sources", "GoogleCloudTestV1")
151+
for _, expected := range []string{"Color.swift", "Kind.swift"} {
152+
filename := filepath.Join(expectedDir, expected)
153+
if _, err := os.Stat(filename); errors.Is(err, fs.ErrNotExist) {
154+
t.Errorf("missing %s: %s", filename, err)
155+
}
156+
}
157+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{{!
2+
Copyright 2026 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
}}
16+
//
17+
// Copyright {{Codec.CopyrightYear}} Google LLC
18+
{{#Codec.BoilerPlate}}
19+
//{{{.}}}
20+
{{/Codec.BoilerPlate}}
21+
22+
import Foundation
23+
24+
{{#Codec.DocLines}}
25+
/// {{{.}}}
26+
{{/Codec.DocLines}}
27+
public enum {{Codec.Name}}: Codable, Equatable {
28+
}

0 commit comments

Comments
 (0)