Skip to content

Commit 8672c8f

Browse files
authored
feat(sidekick/swift): bootstrap method generation (#5146)
Generate a skeleton of each method, the skeleton just crashes the application (think `assert()`) for now. It should have the correct return type for the APIs we want to generate, but does not support `google.protobuf.Empty`, or pagination, or LROs yet.
1 parent f8836b2 commit 8672c8f

4 files changed

Lines changed: 134 additions & 1 deletion

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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 methodAnnotations struct {
22+
Name string
23+
DocLines []string
24+
}
25+
26+
func (codec *codec) annotateMethod(method *api.Method) {
27+
docLines := codec.formatDocumentation(method.Documentation)
28+
annotations := &methodAnnotations{
29+
Name: camelCase(method.Name),
30+
DocLines: docLines,
31+
}
32+
method.Codec = annotations
33+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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/googleapis/librarian/internal/sidekick/api"
22+
)
23+
24+
func TestAnnotateMethod(t *testing.T) {
25+
method := &api.Method{
26+
Name: "CreateSecret",
27+
Documentation: "Creates a secret.",
28+
}
29+
service := &api.Service{
30+
Name: "SecretManagerService",
31+
Methods: []*api.Method{method},
32+
}
33+
model := api.NewTestAPI(nil, nil, []*api.Service{service})
34+
codec := newTestCodec(t, model, nil)
35+
36+
if err := codec.annotateModel(); err != nil {
37+
t.Fatal(err)
38+
}
39+
40+
want := &methodAnnotations{
41+
Name: "createSecret",
42+
DocLines: []string{"Creates a secret."},
43+
}
44+
45+
if diff := cmp.Diff(want, method.Codec); diff != "" {
46+
t.Errorf("mismatch (-want +got):\n%s", diff)
47+
}
48+
}
49+
50+
func TestAnnotateMethod_EscapedName(t *testing.T) {
51+
for _, test := range []struct {
52+
name string
53+
methodName string
54+
wantName string
55+
}{
56+
{"escaped func", "Func", "`func`"},
57+
{"escaped self", "Self", "self_"},
58+
{"escaped default", "Default", "`default`"},
59+
} {
60+
t.Run(test.name, func(t *testing.T) {
61+
method := &api.Method{
62+
Name: test.methodName,
63+
Documentation: "Test documentation.",
64+
}
65+
service := &api.Service{
66+
Name: "TestService",
67+
Methods: []*api.Method{method},
68+
}
69+
model := api.NewTestAPI(nil, nil, []*api.Service{service})
70+
codec := newTestCodec(t, model, nil)
71+
72+
if err := codec.annotateModel(); err != nil {
73+
t.Fatal(err)
74+
}
75+
76+
want := &methodAnnotations{
77+
Name: test.wantName,
78+
DocLines: []string{"Test documentation."},
79+
}
80+
81+
if diff := cmp.Diff(want, method.Codec); diff != "" {
82+
t.Errorf("mismatch (-want +got):\n%s", diff)
83+
}
84+
})
85+
}
86+
}

internal/sidekick/swift/annotate_service.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,7 @@ func (codec *codec) annotateService(service *api.Service, model *modelAnnotation
3535
}
3636

3737
service.Codec = annotations
38+
for _, method := range service.Methods {
39+
codec.annotateMethod(method)
40+
}
3841
}

internal/sidekick/swift/templates/common/service.swift.mustache

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,16 @@ import Foundation
2626
/// {{{.}}}
2727
{{/Codec.DocLines}}
2828
public class {{Codec.Name}} {
29-
{{! TODO(#....) - add method generation }}
29+
{{#Methods}}
30+
31+
{{#Codec.DocLines}}
32+
/// {{{.}}}
33+
{{/Codec.DocLines}}
34+
public func {{Codec.Name}}(request: {{InputType.Codec.Name}}) async throws
35+
{{! TODO(#5138) - deal with `google.proto.Empty` and other complex return types }}
36+
-> {{OutputType.Codec.Name}}
37+
{
38+
fatalError("Unimplemented")
39+
}
40+
{{/Methods}}
3041
}

0 commit comments

Comments
 (0)