Skip to content

Commit f0dc14a

Browse files
xaionaro@dx.centerxaionaro@dx.center
authored andcommitted
Add service methods subcommand to list interface methods
Adds `aidlcli service methods <name>` which connects to a binder service, queries its interface descriptor, and looks up the methods in the generated registry. Supports both text and JSON output formats.
1 parent a479820 commit f0dc14a

2 files changed

Lines changed: 188 additions & 0 deletions

File tree

tools/cmd/aidlcli/cmd_service.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"os"
1010
"strconv"
11+
"strings"
1112

1213
"github.com/spf13/cobra"
1314
"github.com/xaionaro-go/aidl/binder"
@@ -23,6 +24,7 @@ func newServiceCmd() *cobra.Command {
2324
cmd.AddCommand(newServiceListCmd())
2425
cmd.AddCommand(newServiceInspectCmd())
2526
cmd.AddCommand(newServiceTransactCmd())
27+
cmd.AddCommand(newServiceMethodsCmd())
2628

2729
return cmd
2830
}
@@ -199,3 +201,111 @@ func queryDescriptor(
199201

200202
return desc
201203
}
204+
205+
func newServiceMethodsCmd() *cobra.Command {
206+
return &cobra.Command{
207+
Use: "methods <name>",
208+
Short: "List methods of a binder service interface",
209+
Args: cobra.ExactArgs(1),
210+
RunE: func(cmd *cobra.Command, args []string) error {
211+
ctx := cmd.Context()
212+
name := args[0]
213+
214+
conn, err := OpenConn(ctx, cmd)
215+
if err != nil {
216+
return err
217+
}
218+
defer conn.Close(ctx)
219+
220+
svc, err := conn.GetService(ctx, name)
221+
if err != nil {
222+
return err
223+
}
224+
225+
descriptor := queryDescriptor(ctx, svc)
226+
227+
mode, err := cmd.Root().PersistentFlags().GetString("format")
228+
if err != nil {
229+
return fmt.Errorf("reading --format flag: %w", err)
230+
}
231+
232+
if generatedRegistry == nil {
233+
fmt.Fprintf(os.Stdout, "unknown interface %s — no methods in registry\n", descriptor)
234+
return nil
235+
}
236+
237+
info := generatedRegistry.ByDescriptor(descriptor)
238+
if info == nil {
239+
fmt.Fprintf(os.Stdout, "unknown interface %s — no methods in registry\n", descriptor)
240+
return nil
241+
}
242+
243+
f := NewFormatter(mode, os.Stdout)
244+
switch f.Mode {
245+
case "json":
246+
f.writeJSON(map[string]any{
247+
"descriptor": descriptor,
248+
"methods": methodsToJSON(info.Methods),
249+
})
250+
default:
251+
fmt.Fprintf(f.W, "Interface: %s (%d methods)\n\n", descriptor, len(info.Methods))
252+
for _, m := range info.Methods {
253+
fmt.Fprintf(f.W, " %s\n", formatMethodSignature(m))
254+
}
255+
}
256+
257+
return nil
258+
},
259+
}
260+
}
261+
262+
// formatMethodSignature renders a MethodInfo as "kebab-name(params...) -> returnType".
263+
func formatMethodSignature(m MethodInfo) string {
264+
var b strings.Builder
265+
b.WriteString(camelToKebab(m.Name))
266+
b.WriteByte('(')
267+
for i, p := range m.Params {
268+
if i > 0 {
269+
b.WriteString(", ")
270+
}
271+
b.WriteString(p.Name)
272+
b.WriteByte(' ')
273+
b.WriteString(p.Type)
274+
}
275+
b.WriteByte(')')
276+
277+
if m.ReturnType != "" {
278+
b.WriteString(" -> ")
279+
b.WriteString(m.ReturnType)
280+
}
281+
282+
return b.String()
283+
}
284+
285+
// methodsToJSON converts a slice of MethodInfo into a JSON-friendly representation.
286+
func methodsToJSON(methods []MethodInfo) []map[string]any {
287+
result := make([]map[string]any, 0, len(methods))
288+
for _, m := range methods {
289+
entry := map[string]any{
290+
"name": camelToKebab(m.Name),
291+
}
292+
293+
if len(m.Params) > 0 {
294+
params := make([]map[string]string, 0, len(m.Params))
295+
for _, p := range m.Params {
296+
params = append(params, map[string]string{
297+
"name": p.Name,
298+
"type": p.Type,
299+
})
300+
}
301+
entry["params"] = params
302+
}
303+
304+
if m.ReturnType != "" {
305+
entry["return_type"] = m.ReturnType
306+
}
307+
308+
result = append(result, entry)
309+
}
310+
return result
311+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//go:build linux
2+
3+
package main
4+
5+
import (
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestFormatMethodSignature_NoParams_NoReturn(t *testing.T) {
13+
m := MethodInfo{Name: "ForceFlushCommands"}
14+
assert.Equal(t, "force-flush-commands()", formatMethodSignature(m))
15+
}
16+
17+
func TestFormatMethodSignature_NoParams_WithReturn(t *testing.T) {
18+
m := MethodInfo{Name: "IsUserAMonkey", ReturnType: "bool"}
19+
assert.Equal(t, "is-user-a-monkey() -> bool", formatMethodSignature(m))
20+
}
21+
22+
func TestFormatMethodSignature_WithParams_WithReturn(t *testing.T) {
23+
m := MethodInfo{
24+
Name: "CheckPermission",
25+
Params: []ParamInfo{
26+
{Name: "permission", Type: "string"},
27+
{Name: "pid", Type: "int32"},
28+
{Name: "uid", Type: "int32"},
29+
},
30+
ReturnType: "int32",
31+
}
32+
assert.Equal(t,
33+
"check-permission(permission string, pid int32, uid int32) -> int32",
34+
formatMethodSignature(m),
35+
)
36+
}
37+
38+
func TestFormatMethodSignature_WithParams_NoReturn(t *testing.T) {
39+
m := MethodInfo{
40+
Name: "SetByte",
41+
Params: []ParamInfo{{Name: "input", Type: "byte"}},
42+
}
43+
assert.Equal(t, "set-byte(input byte)", formatMethodSignature(m))
44+
}
45+
46+
func TestMethodsToJSON(t *testing.T) {
47+
methods := []MethodInfo{
48+
{Name: "IsUserAMonkey", ReturnType: "bool"},
49+
{
50+
Name: "CheckPermission",
51+
Params: []ParamInfo{
52+
{Name: "permission", Type: "string"},
53+
{Name: "pid", Type: "int32"},
54+
},
55+
ReturnType: "int32",
56+
},
57+
{Name: "Restart"},
58+
}
59+
60+
result := methodsToJSON(methods)
61+
require.Len(t, result, 3)
62+
63+
assert.Equal(t, "is-user-a-monkey", result[0]["name"])
64+
assert.Equal(t, "bool", result[0]["return_type"])
65+
assert.Nil(t, result[0]["params"])
66+
67+
assert.Equal(t, "check-permission", result[1]["name"])
68+
assert.Equal(t, "int32", result[1]["return_type"])
69+
params, ok := result[1]["params"].([]map[string]string)
70+
require.True(t, ok)
71+
require.Len(t, params, 2)
72+
assert.Equal(t, "permission", params[0]["name"])
73+
assert.Equal(t, "string", params[0]["type"])
74+
75+
assert.Equal(t, "restart", result[2]["name"])
76+
assert.Nil(t, result[2]["return_type"])
77+
assert.Nil(t, result[2]["params"])
78+
}

0 commit comments

Comments
 (0)