Skip to content

Commit 0b5084a

Browse files
committed
cmd/cue/cmd: hook up protobuf encodings types
Closes #606 Change-Id: I712ed6f9fc37d53b633a44ed87e4c71c1e018b0f Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9372 Reviewed-by: CUE cueckoo <[email protected]> Reviewed-by: Marcel van Lohuizen <[email protected]>
1 parent 362e3a5 commit 0b5084a

File tree

10 files changed

+275
-42
lines changed

10 files changed

+275
-42
lines changed

cmd/cue/cmd/common.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ func (i *streamingIterator) scan() bool {
296296
if schema := i.b.encConfig.Schema; schema.Exists() {
297297
i.e = schema.Err()
298298
if i.e == nil {
299-
i.v = i.v.Unify(schema)
299+
i.v = i.v.Unify(schema) // TODO(required fields): don't merge in schema
300300
i.e = i.v.Err()
301301
}
302302
i.f = nil
@@ -409,6 +409,19 @@ func (p *buildPlan) getDecoders(b *build.Instance) (schemas, values []*decoderIn
409409
for _, f := range b.OrphanedFiles {
410410
switch f.Encoding {
411411
case build.Protobuf, build.YAML, build.JSON, build.JSONL, build.Text:
412+
if f.Interpretation == build.ProtobufJSON {
413+
// Need a schema.
414+
values = append(values, &decoderInfo{f, nil})
415+
continue
416+
}
417+
case build.TextProto:
418+
if p.importing {
419+
return schemas, values, errors.Newf(token.NoPos,
420+
"cannot import textproto files")
421+
}
422+
// Needs to be decoded after any schema.
423+
values = append(values, &decoderInfo{f, nil})
424+
continue
412425
default:
413426
return schemas, values, errors.Newf(token.NoPos,
414427
"unsupported encoding %q", f.Encoding)
@@ -569,13 +582,17 @@ func parseArgs(cmd *Command, args []string, cfg *config) (p *buildPlan, err erro
569582
}
570583

571584
switch {
585+
default:
586+
fallthrough
587+
588+
case p.schema != nil:
589+
p.orphaned = values
590+
572591
case p.mergeData, p.usePlacement(), p.importing:
573592
if err = p.placeOrphans(b, values); err != nil {
574593
return nil, err
575594
}
576595

577-
default:
578-
p.orphaned = values
579596
}
580597

581598
if len(b.Files) > 0 {

cmd/cue/cmd/help.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,11 @@ var filetypeHelp = &cobra.Command{
185185
jsonl .jsonl/.ldjson Line-separated JSON values.
186186
jsonschema JSON Schema.
187187
openapi OpenAPI schema.
188+
pb Use Protobuf mappings (e.g. json+pb)
189+
textproto .textproto Text-based protocol buffers.
188190
proto .proto Protocol Buffer definitions.
189-
go .go Go source files.
190-
text .txt Raw text file; the evaluated
191+
go .go Go source files.
192+
text .txt Raw text file; the evaluated
191193
value must be of type string.
192194
193195
OpenAPI, JSON Schema and Protocol Buffer definitions are
@@ -495,7 +497,6 @@ cuelang.org/go/pkg/tool/tool.cue.
495497
// - id=<url>
496498

497499
// TODO: filetypes:
498-
// - textpb
499500
// - binpb
500501

501502
// TODO: cue.mod help topic

cmd/cue/cmd/orphans.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"cuelang.org/go/cue/errors"
2828
"cuelang.org/go/cue/parser"
2929
"cuelang.org/go/cue/token"
30+
"cuelang.org/go/encoding/protobuf/jsonpb"
3031
"cuelang.org/go/internal"
3132
"cuelang.org/go/internal/astinternal"
3233
"cuelang.org/go/internal/encoding"
@@ -172,6 +173,7 @@ func placeOrphans(b *buildPlan, d *encoding.Decoder, pkg string, objs ...*ast.Fi
172173
expr := internal.ToExpr(file)
173174
p, _, _ := internal.PackageInfo(file)
174175

176+
var path cue.Path
175177
var labels []ast.Label
176178

177179
switch {
@@ -227,6 +229,22 @@ func placeOrphans(b *buildPlan, d *encoding.Decoder, pkg string, objs ...*ast.Fi
227229
a = append(a, cue.Label(label))
228230
labels = append(labels, label)
229231
}
232+
233+
path = cue.MakePath(a...)
234+
}
235+
236+
switch d.Interpretation() {
237+
case build.ProtobufJSON:
238+
v := b.instance.Value().LookupPath(path)
239+
if b.useList {
240+
v, _ = v.Elem()
241+
}
242+
if !v.Exists() {
243+
break
244+
}
245+
if err := jsonpb.NewDecoder(v).RewriteFile(file); err != nil {
246+
return nil, err
247+
}
230248
}
231249

232250
if b.useList {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
cue eval schema.cue json+pb: data.json
2+
cmp stdout out/data1
3+
4+
cue eval schemaflag.cue -d '#X' json+pb: data.json
5+
cmp stdout out/data1
6+
7+
! cue eval schema.cue json+pb: data-err.json
8+
cmp stderr out/data-err
9+
10+
cue eval .:nested yaml+pb: stream.yaml -l kind
11+
cmp stdout out/stream
12+
13+
-- schema.cue --
14+
a: int @protobuf(1,int64) // to string
15+
b: int @protobuf(2,int32) // also allow string
16+
c: int // also allow
17+
d: float
18+
s: string
19+
t: bytes
20+
21+
-- nested.cue --
22+
package nested
23+
24+
A: {
25+
a: int @protobuf(1,int64) // to string
26+
b: bytes
27+
}
28+
29+
B: {
30+
a: int @protobuf(1,int64) // to string
31+
s: string
32+
}
33+
34+
-- schemaflag.cue --
35+
#X: {
36+
a: int @protobuf(1,int64) // to string
37+
b: int @protobuf(2,int32) // also allow string
38+
c: int // also allow
39+
d: float
40+
s: string
41+
t: bytes
42+
}
43+
44+
-- data.json --
45+
{"a": "10", "b": "20", "c": 30, "d": "1.2",
46+
"s":"SGVsbG8sIOS4lueVjA==",
47+
"t": "SGVsbG8sIOS4lueVjA=="}
48+
49+
-- data-err.json --
50+
{"a": "10", "b": "20", "c": "30", "t": "SGVsbG8sIOS4lue???VjA==" }
51+
52+
-- stream.yaml --
53+
kind: "A"
54+
a: "10"
55+
b: "SGVsbG8sIOS4lueVjA=="
56+
---
57+
kind: "B"
58+
a: "10"
59+
s: "SGVsbG8sIOS4lueVjA=="
60+
61+
-- out/data1 --
62+
a: 10
63+
b: 20
64+
c: 30
65+
d: 1.2
66+
s: "SGVsbG8sIOS4lueVjA=="
67+
t: 'Hello, 世界'
68+
-- out/stream --
69+
A: {
70+
kind: "A"
71+
a: 10
72+
b: 'Hello, 世界'
73+
}
74+
B: {
75+
kind: "B"
76+
a: 10
77+
s: "SGVsbG8sIOS4lueVjA=="
78+
}
79+
-- out/data-err --
80+
t: failed to decode base64: illegal base64 data at input byte 15:
81+
./data-err.json:1:40
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
cue eval topschema.cue foo.textproto
2+
cmp stdout out/topfoo.textproto
3+
4+
cue eval -d '#X' schema.cue foo.textproto
5+
cmp stdout out/foo.textproto
6+
7+
! cue eval -d '#X' schema.cue foo.textproto -l c
8+
cmp stderr out/stderr3
9+
10+
-- topschema.cue --
11+
a: int
12+
b: [...int]
13+
c: string
14+
-- schema.cue --
15+
#X: {
16+
a: int
17+
b: [...int]
18+
c: string
19+
}
20+
-- foo.textproto --
21+
a: 4
22+
b: 1
23+
b: 2
24+
b: 3
25+
b: 4
26+
b: 5
27+
c: "foo"
28+
-- out/topfoo.textproto --
29+
a: 4
30+
b: [1, 2, 3, 4, 5]
31+
c: "foo"
32+
-- out/foo.textproto --
33+
a: 4
34+
b: [1, 2, 3, 4, 5]
35+
c: "foo"
36+
-- out/stderr3 --
37+
cannot combine --schema flag with flag "path", "list", or "files"

cue/build/file.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,17 @@ type File struct {
3030
type Encoding string
3131

3232
const (
33-
CUE Encoding = "cue"
34-
JSON Encoding = "json"
35-
YAML Encoding = "yaml"
36-
JSONL Encoding = "jsonl"
37-
Text Encoding = "text"
38-
Protobuf Encoding = "proto"
33+
CUE Encoding = "cue"
34+
JSON Encoding = "json"
35+
YAML Encoding = "yaml"
36+
JSONL Encoding = "jsonl"
37+
Text Encoding = "text"
38+
Protobuf Encoding = "proto"
39+
TextProto Encoding = "textproto"
40+
BinaryProto Encoding = "pb"
3941

4042
// TODO:
4143
// TOML
42-
// TextProto
43-
// BinProto
4444

4545
Code Encoding = "code" // Programming languages
4646
)
@@ -62,9 +62,10 @@ const (
6262
// the info.title and info.version fields.
6363
//
6464
// In all other cases, the underlying data is interpreted as is.
65-
Auto Interpretation = "auto"
66-
JSONSchema Interpretation = "jsonschema"
67-
OpenAPI Interpretation = "openapi"
65+
Auto Interpretation = "auto"
66+
JSONSchema Interpretation = "jsonschema"
67+
OpenAPI Interpretation = "openapi"
68+
ProtobufJSON Interpretation = "pb"
6869
)
6970

7071
// A Form specifies the form in which a program should be represented.

internal/encoding/encoder.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import (
3030
"cuelang.org/go/cue/format"
3131
"cuelang.org/go/cue/token"
3232
"cuelang.org/go/encoding/openapi"
33+
"cuelang.org/go/encoding/protobuf/jsonpb"
34+
"cuelang.org/go/encoding/protobuf/textproto"
3335
"cuelang.org/go/internal"
3436
"cuelang.org/go/internal/filetypes"
3537
"cuelang.org/go/pkg/encoding/yaml"
@@ -87,6 +89,12 @@ func NewEncoder(f *build.File, cfg *Config) (*Encoder, error) {
8789
}
8890
return openapi.Generate(i, cfg)
8991
}
92+
case build.ProtobufJSON:
93+
e.interpret = func(v cue.Value) (*ast.File, error) {
94+
f := valueToFile(v)
95+
return f, jsonpb.NewEncoder(v).RewriteFile(f)
96+
}
97+
9098
// case build.JSONSchema:
9199
// // TODO: get encoding options
92100
// cfg := openapi.Config{}
@@ -182,6 +190,20 @@ func NewEncoder(f *build.File, cfg *Config) (*Encoder, error) {
182190
return err
183191
}
184192

193+
case build.TextProto:
194+
// TODO: verify that the schema is given. Otherwise err out.
195+
e.concrete = true
196+
e.encValue = func(v cue.Value) error {
197+
v = v.Unify(cfg.Schema)
198+
b, err := textproto.NewEncoder().Encode(v)
199+
if err != nil {
200+
return err
201+
}
202+
203+
_, err = w.Write(b)
204+
return err
205+
}
206+
185207
case build.Text:
186208
e.concrete = true
187209
e.encValue = func(v cue.Value) error {

internal/encoding/encoding.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import (
3737
"cuelang.org/go/encoding/jsonschema"
3838
"cuelang.org/go/encoding/openapi"
3939
"cuelang.org/go/encoding/protobuf"
40+
"cuelang.org/go/encoding/protobuf/jsonpb"
41+
"cuelang.org/go/encoding/protobuf/textproto"
4042
"cuelang.org/go/internal"
4143
"cuelang.org/go/internal/filetypes"
4244
"cuelang.org/go/internal/third_party/yaml"
@@ -48,6 +50,7 @@ type Decoder struct {
4850
cfg *Config
4951
closer io.Closer
5052
next func() (ast.Expr, error)
53+
rewriteFunc rewriteFunc
5154
interpretFunc interpretFunc
5255
interpretation build.Interpretation
5356
expr ast.Expr
@@ -59,6 +62,7 @@ type Decoder struct {
5962
}
6063

6164
type interpretFunc func(*cue.Instance) (file *ast.File, id string, err error)
65+
type rewriteFunc func(*ast.File) (file *ast.File, err error)
6266

6367
// ID returns a canonical identifier for the decoded object or "" if no such
6468
// identifier could be found.
@@ -89,7 +93,15 @@ func (i *Decoder) Next() {
8993
}
9094

9195
func (i *Decoder) doInterpret() {
92-
// Interpretations
96+
if i.rewriteFunc != nil {
97+
i.file = i.File()
98+
var err error
99+
i.file, err = i.rewriteFunc(i.file)
100+
if err != nil {
101+
i.err = err
102+
return
103+
}
104+
}
93105
if i.interpretFunc != nil {
94106
var r cue.Runtime
95107
i.file = i.File()
@@ -208,6 +220,9 @@ func NewDecoder(f *build.File, cfg *Config) *Decoder {
208220
case build.JSONSchema:
209221
i.interpretation = build.JSONSchema
210222
i.interpretFunc = jsonSchemaFunc(cfg, f)
223+
case build.ProtobufJSON:
224+
i.interpretation = build.ProtobufJSON
225+
i.rewriteFunc = protobufJSONFunc(cfg, f)
211226
default:
212227
i.err = fmt.Errorf("unsupported interpretation %q", f.Interpretation)
213228
}
@@ -242,6 +257,13 @@ func NewDecoder(f *build.File, cfg *Config) *Decoder {
242257
PkgName: cfg.PkgName,
243258
}
244259
i.file, i.err = protobuf.Extract(path, r, paths)
260+
case build.TextProto:
261+
b, err := ioutil.ReadAll(r)
262+
i.err = err
263+
if err == nil {
264+
d := textproto.NewDecoder()
265+
i.expr, i.err = d.Parse(cfg.Schema, path, b)
266+
}
245267
default:
246268
i.err = fmt.Errorf("unsupported encoding %q", f.Encoding)
247269
}
@@ -286,6 +308,16 @@ func openAPIFunc(c *Config, f *build.File) interpretFunc {
286308
}
287309
}
288310

311+
func protobufJSONFunc(cfg *Config, file *build.File) rewriteFunc {
312+
return func(f *ast.File) (*ast.File, error) {
313+
if !cfg.Schema.Exists() {
314+
return f, errors.Newf(token.NoPos,
315+
"no schema specified for protobuf interpretation.")
316+
}
317+
return f, jsonpb.NewDecoder(cfg.Schema).RewriteFile(f)
318+
}
319+
}
320+
289321
func reader(f *build.File, stdin io.Reader) (io.ReadCloser, error) {
290322
switch s := f.Source.(type) {
291323
case nil:

0 commit comments

Comments
 (0)