@@ -12,9 +12,12 @@ import (
12
12
"github.com/spf13/cobra"
13
13
"github.com/spf13/pflag"
14
14
"github.com/stoewer/go-strcase"
15
+ "go.einride.tech/aip/reflect/aipreflect"
16
+ "google.golang.org/genproto/googleapis/api/annotations"
15
17
"google.golang.org/protobuf/encoding/protojson"
16
18
"google.golang.org/protobuf/proto"
17
19
"google.golang.org/protobuf/reflect/protoreflect"
20
+ "google.golang.org/protobuf/reflect/protoregistry"
18
21
)
19
22
20
23
// NewServiceCommand initializes a new *cobra.Command for the provided gRPC service.
@@ -44,7 +47,7 @@ func NewMethodCommand(
44
47
}
45
48
fromFile := cmd .Flags ().StringP ("from-file" , "f" , "" , "path to a JSON file containing the request payload" )
46
49
_ = cmd .MarkFlagFilename ("from-file" , "json" )
47
- setFlags (comments , cmd . Flags () , nil , in .ProtoReflect ().Descriptor (), in .ProtoReflect )
50
+ setFlags (comments , cmd , nil , in .ProtoReflect ().Descriptor (), in .ProtoReflect )
48
51
cmd .RunE = func (cmd * cobra.Command , args []string ) error {
49
52
if cmd .Flags ().Changed ("from-file" ) {
50
53
data , err := os .ReadFile (* fromFile )
@@ -55,7 +58,7 @@ func NewMethodCommand(
55
58
return err
56
59
}
57
60
}
58
- conn , err := Dial (cmd .Context ())
61
+ conn , err := dial (cmd .Context ())
59
62
if err != nil {
60
63
return err
61
64
}
@@ -91,7 +94,7 @@ func methodURI(method protoreflect.MethodDescriptor) string {
91
94
92
95
func setFlags (
93
96
comments map [protoreflect.FullName ]string ,
94
- flags * pflag. FlagSet ,
97
+ cmd * cobra. Command ,
95
98
parentFields []protoreflect.FieldDescriptor ,
96
99
msg protoreflect.MessageDescriptor ,
97
100
mutable func () protoreflect.Message ,
@@ -105,37 +108,48 @@ func setFlags(
105
108
if field .IsList () {
106
109
// TODO: Implement support for repeated durations.
107
110
} else {
108
- flags .AddFlag (& pflag.Flag {
109
- Name : flagName (field , parentFields ),
110
- Usage : flagUsage (comments [field .FullName ()]),
111
- Value : durationValue {mutable : mutable , field : field },
111
+ addFlag (cmd , field , parentFields , comments [field .FullName ()], durationValue {
112
+ mutable : mutable ,
113
+ field : field ,
112
114
})
113
115
}
114
116
case "google.protobuf.Timestamp" :
115
117
if field .IsList () {
116
118
// TODO: Implement support for repeated timestamps.
117
119
} else {
118
- flags .AddFlag (& pflag.Flag {
119
- Name : flagName (field , parentFields ),
120
- Usage : flagUsage (comments [field .FullName ()]),
121
- Value : timestampValue {mutable : mutable , field : field },
120
+ addFlag (cmd , field , parentFields , comments [field .FullName ()], timestampValue {
121
+ mutable : mutable ,
122
+ field : field ,
122
123
})
123
124
}
124
125
case "google.protobuf.FieldMask" :
125
126
if field .IsList () {
126
127
// Repeated field masks is intentionally not supported.
127
128
} else {
128
- flags .AddFlag (& pflag.Flag {
129
- Name : flagName (field , parentFields ),
130
- Usage : flagUsage (comments [field .FullName ()]),
131
- Value : fieldMaskValue {mutable : mutable , field : field },
129
+ addFlag (cmd , field , parentFields , comments [field .FullName ()], fieldMaskValue {
130
+ mutable : mutable ,
131
+ field : field ,
132
132
})
133
133
}
134
134
default :
135
- if field .Cardinality () != protoreflect .Repeated {
135
+ switch {
136
+ case field .IsMap ():
137
+ switch {
138
+ case field .MapKey ().Kind () == protoreflect .StringKind &&
139
+ field .MapValue ().Kind () == protoreflect .StringKind :
140
+ addFlag (cmd , field , parentFields , comments [field .FullName ()], mapStringStringValue {
141
+ mutable : mutable ,
142
+ field : field ,
143
+ })
144
+ default :
145
+ // TODO: Implement support for more map types.
146
+ }
147
+ case field .IsList ():
148
+ // Repeated nested messages not supported.
149
+ default :
136
150
setFlags (
137
151
comments ,
138
- flags ,
152
+ cmd ,
139
153
append (parentFields , field ),
140
154
field .Message (),
141
155
func () protoreflect.Message {
@@ -144,16 +158,25 @@ func setFlags(
144
158
)
145
159
}
146
160
}
161
+ case protoreflect .EnumKind :
162
+ if field .IsList () {
163
+ // TODO: Implement support for repeated enums.
164
+ } else {
165
+ addFlag (cmd , field , parentFields , comments [field .FullName ()], enumValue {
166
+ mutable : mutable ,
167
+ field : field ,
168
+ })
169
+ }
147
170
case protoreflect .StringKind , protoreflect .BoolKind , protoreflect .BytesKind , protoreflect .DoubleKind ,
148
171
protoreflect .FloatKind , protoreflect .Int64Kind , protoreflect .Int32Kind :
149
- setPrimitiveFlag (comments , flags , parentFields , mutable , field )
172
+ setPrimitiveFlag (comments , cmd , parentFields , mutable , field )
150
173
}
151
174
}
152
175
}
153
176
154
177
func setPrimitiveFlag (
155
178
comments map [protoreflect.FullName ]string ,
156
- flags * pflag. FlagSet ,
179
+ cmd * cobra. Command ,
157
180
parentFields []protoreflect.FieldDescriptor ,
158
181
mutable func () protoreflect.Message ,
159
182
field protoreflect.FieldDescriptor ,
@@ -224,11 +247,120 @@ func setPrimitiveFlag(
224
247
default :
225
248
panic (fmt .Errorf ("unhandled primitive kind: %v" , field .Kind ())) // shouldn't happen
226
249
}
227
- flags .AddFlag (& pflag.Flag {
250
+ addFlag (cmd , field , parentFields , comments [field .FullName ()], value )
251
+ }
252
+
253
+ func addFlag (
254
+ cmd * cobra.Command ,
255
+ field protoreflect.FieldDescriptor ,
256
+ parentFields []protoreflect.FieldDescriptor ,
257
+ comment string ,
258
+ value pflag.Value ,
259
+ ) {
260
+ flag := & pflag.Flag {
228
261
Name : flagName (field , parentFields ),
229
- Usage : flagUsage ( comments [ field . FullName ()] ),
262
+ Usage : trimComment ( comment ),
230
263
Value : value ,
231
- })
264
+ }
265
+ cmd .Flags ().AddFlag (flag )
266
+ maybeMarkHidden (cmd , flag , field )
267
+ maybeMarkRequired (cmd , flag , field )
268
+ maybeRegisterResourceReferenceCompletionFunction (cmd , flag , field )
269
+ maybeRegisterResourceNameCompletionFunction (cmd , flag , field )
270
+ }
271
+
272
+ func maybeMarkHidden (
273
+ cmd * cobra.Command ,
274
+ flag * pflag.Flag ,
275
+ field protoreflect.FieldDescriptor ,
276
+ ) {
277
+ if fieldBehaviors , ok := proto .GetExtension (
278
+ field .Options (),
279
+ annotations .E_FieldBehavior ,
280
+ ).([]annotations.FieldBehavior ); ok {
281
+ for _ , fieldBehavior := range fieldBehaviors {
282
+ if fieldBehavior == annotations .FieldBehavior_OUTPUT_ONLY {
283
+ _ = cmd .Flags ().MarkHidden (flag .Name )
284
+ }
285
+ }
286
+ }
287
+ }
288
+
289
+ func maybeMarkRequired (
290
+ cmd * cobra.Command ,
291
+ flag * pflag.Flag ,
292
+ field protoreflect.FieldDescriptor ,
293
+ ) {
294
+ if fieldBehaviors , ok := proto .GetExtension (
295
+ field .Options (),
296
+ annotations .E_FieldBehavior ,
297
+ ).([]annotations.FieldBehavior ); ok {
298
+ for _ , fieldBehavior := range fieldBehaviors {
299
+ if fieldBehavior == annotations .FieldBehavior_REQUIRED {
300
+ _ = cmd .MarkFlagRequired (flag .Name )
301
+ }
302
+ }
303
+ }
304
+ }
305
+
306
+ func maybeRegisterResourceReferenceCompletionFunction (
307
+ cmd * cobra.Command ,
308
+ flag * pflag.Flag ,
309
+ field protoreflect.FieldDescriptor ,
310
+ ) {
311
+ if field .Kind () == protoreflect .StringKind {
312
+ if resourceReference , ok := proto .GetExtension (
313
+ field .Options (),
314
+ annotations .E_ResourceReference ,
315
+ ).(* annotations.ResourceReference ); ok && resourceReference .GetType () != "" {
316
+ completionFunc := resourceNameCompletionFunc
317
+ if field .IsList () {
318
+ completionFunc = resourceNameListCompletionFunc
319
+ }
320
+ aipreflect .RangeResourceDescriptorsInPackage (
321
+ protoregistry .GlobalFiles ,
322
+ field .ParentFile ().Package (),
323
+ func (resource * annotations.ResourceDescriptor ) bool {
324
+ if resource .GetType () == resourceReference .GetType () && len (resource .GetPattern ()) > 0 {
325
+ _ = cmd .RegisterFlagCompletionFunc (
326
+ flag .Name ,
327
+ completionFunc (resource .GetPattern ()... ),
328
+ )
329
+ return false
330
+ }
331
+ return true
332
+ },
333
+ )
334
+ }
335
+ }
336
+ }
337
+
338
+ func maybeRegisterResourceNameCompletionFunction (
339
+ cmd * cobra.Command ,
340
+ flag * pflag.Flag ,
341
+ field protoreflect.FieldDescriptor ,
342
+ ) {
343
+ if ! field .IsList () && field .Name () == "name" {
344
+ if resourceDescriptor , ok := proto .GetExtension (
345
+ field .Parent ().Options (),
346
+ annotations .E_Resource ,
347
+ ).(* annotations.ResourceDescriptor ); ok && resourceDescriptor .GetType () != "" {
348
+ aipreflect .RangeResourceDescriptorsInPackage (
349
+ protoregistry .GlobalFiles ,
350
+ field .ParentFile ().Package (),
351
+ func (resource * annotations.ResourceDescriptor ) bool {
352
+ if resource .GetType () == resourceDescriptor .GetType () && len (resource .GetPattern ()) > 0 {
353
+ _ = cmd .RegisterFlagCompletionFunc (
354
+ flag .Name ,
355
+ resourceNameCompletionFunc (resource .GetPattern ()... ),
356
+ )
357
+ return false
358
+ }
359
+ return true
360
+ },
361
+ )
362
+ }
363
+ }
232
364
}
233
365
234
366
func trimComment (comment string ) string {
0 commit comments