this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

encoding/protobuf: support integer enums

Also support mappings to different representations.

For string disjunctions: associate #enumValue with string

For int disjunctions: map definition references
to symbols.

Change-Id: I7d6dec1798c26a43d40f9c6e8db29b26e8499866
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9402
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>

+723 -151
+2
cmd/cue/cmd/flags.go
··· 43 43 flagPath flagName = "path" 44 44 flagFiles flagName = "files" 45 45 flagProtoPath flagName = "proto_path" 46 + flagProtoEnum flagName = "proto_enum" 46 47 flagWithContext flagName = "with-context" 47 48 flagOut flagName = "out" 48 49 flagOutFile flagName = "outfile" ··· 79 80 f.Bool(string(flagList), false, "concatenate multiple objects into a list") 80 81 f.Bool(string(flagWithContext), false, "import as object with contextual data") 81 82 f.StringArrayP(string(flagProtoPath), "I", nil, "paths in which to search for imports") 83 + f.String(string(flagProtoEnum), "int", "mode for rendering enums (int|json)") 82 84 f.StringP(string(flagGlob), "n", "", "glob filter for file names") 83 85 f.Bool(string(flagMerge), true, "merge non-CUE files") 84 86 }
+43 -14
cmd/cue/cmd/get_go.go
··· 676 676 typ := e.pkg.TypesInfo.TypeOf(v.Name) 677 677 enums := e.consts[typ.String()] 678 678 name := v.Name.Name 679 + mapNamed := false 680 + underlying := e.pkg.TypesInfo.TypeOf(v.Type) 681 + if b, ok := underlying.Underlying().(*types.Basic); ok && b.Kind() != types.String { 682 + mapNamed = true 683 + } 684 + 679 685 switch tn, ok := e.pkg.TypesInfo.Defs[v.Name].(*types.TypeName); { 680 686 case ok: 681 687 if altType := e.altType(tn.Type()); altType != nil { ··· 695 701 a = append(a, e.def(x.Doc, name, s, true)) 696 702 break 697 703 } 698 - // TODO: only print original type if value is not marked as enum. 699 - underlying := e.pkg.TypesInfo.TypeOf(v.Type) 704 + 700 705 f, _ := e.makeField(name, cuetoken.ISA, underlying, x.Doc, true) 701 706 a = append(a, f) 702 707 cueast.SetRelPos(f, cuetoken.NewSection) ··· 708 713 cueast.AddComment(a[len(a)-1], internal.NewComment(false, enumName)) 709 714 710 715 // Constants are mapped as definitions. 711 - var x cueast.Expr = e.ident(enums[0], true) 712 - cueast.SetRelPos(x, cuetoken.Newline) 713 - for _, v := range enums[1:] { 714 - y := e.ident(v, true) 715 - cueast.SetRelPos(y, cuetoken.Newline) 716 - x = cueast.NewBinExpr(cuetoken.OR, x, y) 716 + var exprs []cueast.Expr 717 + var named []cueast.Decl 718 + for _, v := range enums { 719 + label := cueast.NewString(v) 720 + cueast.SetRelPos(label, cuetoken.Blank) 721 + 722 + var x cueast.Expr = e.ident(v, true) 723 + cueast.SetRelPos(x, cuetoken.Newline) 724 + exprs = append(exprs, x) 725 + 726 + if !mapNamed { 727 + continue 728 + } 729 + 730 + named = append(named, &cueast.Field{ 731 + Label: label, 732 + Value: e.ident(v, true), 733 + }) 734 + } 735 + 736 + addField := func(label string, exprs []cueast.Expr) { 737 + f := &cueast.Field{ 738 + Label: cueast.NewIdent(label), 739 + Value: cueast.NewBinExpr(cuetoken.OR, exprs...), 740 + } 741 + cueast.SetRelPos(f, cuetoken.NewSection) 742 + a = append(a, f) 717 743 } 718 - // a = append(a, e.def(nil, enumName, x, true)) 719 - f := &cueast.Field{ 720 - Label: cueast.NewIdent(enumName), 721 - Value: x, 744 + 745 + addField(enumName, exprs) 746 + if len(named) > 0 { 747 + f := &cueast.Field{ 748 + Label: cueast.NewIdent("#values_" + name), 749 + Value: &cueast.StructLit{Elts: named}, 750 + } 751 + cueast.SetRelPos(f, cuetoken.NewSection) 752 + a = append(a, f) 722 753 } 723 - a = append(a, f) 724 - cueast.SetRelPos(f, cuetoken.NewSection) 725 754 } 726 755 } 727 756
+5 -4
cmd/cue/cmd/import.go
··· 334 334 } 335 335 336 336 c := &protobuf.Config{ 337 - Root: root, 338 - Module: module, 339 - Paths: b.encConfig.ProtoPath, 340 - PkgName: b.encConfig.PkgName, 337 + Root: root, 338 + Module: module, 339 + Paths: b.encConfig.ProtoPath, 340 + PkgName: b.encConfig.PkgName, 341 + EnumMode: flagProtoEnum.String(b.cmd), 341 342 } 342 343 if module != "" { 343 344 // We only allow imports from packages within the module if an actual
+28
cmd/cue/cmd/testdata/script/get_go_types.txt
··· 120 120 High 121 121 ) 122 122 123 + type Level2 Level 124 + 125 + const ( 126 + AnotherLevel1 Level2 = iota + 4 127 + AnotherLevel2 128 + ) 129 + 123 130 type CustomJSON struct { 124 131 } 125 132 ··· 432 439 #Medium | 433 440 #High 434 441 442 + #values_Level: { 443 + Unknown: #Unknown 444 + Low: #Low 445 + Medium: #Medium 446 + High: #High 447 + } 448 + 435 449 // Block comment. 436 450 // Indented. 437 451 // ··· 442 456 // Medium is neither High nor Low 443 457 #Medium: #Level & 2 444 458 #High: #Level & 3 459 + 460 + #Level2: #Level // #enumLevel2 461 + 462 + #enumLevel2: 463 + #AnotherLevel1 | 464 + #AnotherLevel2 465 + 466 + #values_Level2: { 467 + AnotherLevel1: #AnotherLevel1 468 + AnotherLevel2: #AnotherLevel2 469 + } 470 + 471 + #AnotherLevel1: #Level2 & 4 472 + #AnotherLevel2: #Level2 & 5 445 473 446 474 #CustomJSON: _ 447 475
+17
cmd/cue/cmd/testdata/script/import_proto.txt
··· 37 37 38 38 // Attributes defines attributes. 39 39 message Attributes { 40 + enum Type { 41 + TYPE_A = 1; 42 + TYPE_B = 2; 43 + } 44 + 40 45 // A map of attribute name to its value. 41 46 map<string, AttributeValue> attributes = 1; 42 47 ··· 171 176 172 177 // Attributes defines attributes. 173 178 #Attributes: { 179 + #Type: 180 + #TYPE_A | 181 + #TYPE_B 182 + 183 + #TYPE_A: 1 184 + #TYPE_B: 2 185 + 186 + #Type_value: { 187 + TYPE_A: 1 188 + TYPE_B: 2 189 + } 190 + 174 191 // A map of attribute name to its value. 175 192 attributes?: { 176 193 [string]: #AttributeValue
+242
cmd/cue/cmd/testdata/script/import_proto2.txt
··· 1 + cd root 2 + cue import proto -I ../include ./... --proto_enum=json 3 + cd .. 4 + 5 + cmp stderr expect-stderr 6 + cmp stdout expect-stdout 7 + cmp root/mixer/v1/attributes_proto_gen.cue expect-attributes_proto_gen.cue 8 + cmp root/mixer/v1/config/client/client_config_proto_gen.cue expect-client_config_proto_gen.cue 9 + cmp root/cue.mod/gen/googleapis.com/acme/test/test_proto_gen.cue expect-test_proto_gen.cue 10 + 11 + -- expect-stdout -- 12 + -- expect-stderr -- 13 + Skipping file "cue.mod/gen/googleapis.com/acme/test/test/test_proto_gen.cue": already exists. 14 + Use -Rf to override. 15 + -- root/cue.mod/module.cue -- 16 + module: "acme.com/api" 17 + 18 + -- root/cue.mod/module.cue -- 19 + module: "acme.com/api" 20 + 21 + -- root/cue.mod/gen/googleapis.com/acme/test/test/test_proto_gen.cue -- 22 + package test_test 23 + 24 + #AnotherTest: { 25 + test?: int32 @protobuf(1) 26 + } 27 + -- root/mixer/v1/attributes.proto -- 28 + syntax = "proto3"; 29 + 30 + import "google/protobuf/timestamp.proto"; 31 + import "acme/test.proto"; 32 + import "acme/test/test.proto"; 33 + 34 + package acme.mixer.v1; 35 + 36 + option go_package = "acme.com/api/mixer/v1"; 37 + 38 + // Attributes defines attributes. 39 + message Attributes { 40 + enum Type { 41 + TYPE_A = 1; 42 + TYPE_B = 2; 43 + } 44 + 45 + // A map of attribute name to its value. 46 + map<string, AttributeValue> attributes = 1; 47 + 48 + // Specifies one attribute value with different type. 49 + message AttributeValue { 50 + // The attribute value. 51 + oneof value { 52 + string string_value = 2; 53 + int64 int64_value = 3; 54 + double double_value = 4; 55 + bool bool_value = 5; 56 + bytes bytes_value = 6; 57 + google.protobuf.Timestamp timestamp_value = 7; 58 + 59 + // Used for values of type STRING_MAP 60 + StringMap string_map_value = 9; 61 + 62 + acme.test.Test test_value = 10; 63 + acme.test.test.AnotherTest test_value = 11; 64 + } 65 + } 66 + 67 + // Defines a string map. 68 + message StringMap { 69 + // Holds a set of name/value pairs. 70 + map<string, string> entries = 1; 71 + } 72 + } 73 + 74 + -- root/mixer/v1/config/client/client_config.proto -- 75 + syntax = "proto3"; 76 + 77 + import "mixer/v1/attributes.proto"; 78 + 79 + // Describes the configuration state for the Mixer client library that's built into Envoy. 80 + package acme.mixer.v1.config.client; 81 + 82 + option go_package = "acme.com/api/mixer/v1/config/client"; 83 + 84 + // Defines the per-service client configuration. 85 + message ServiceConfig { 86 + bool disable_check_calls = 1; 87 + bool disable_report_calls = 2; 88 + Attributes mixer_attributes = 3; 89 + } 90 + -- include/acme/test.proto -- 91 + syntax = "proto3"; 92 + 93 + package acme.test; 94 + 95 + message Test { 96 + int32 test = 1; 97 + } 98 + 99 + -- include/acme/test/test.proto -- 100 + syntax = "proto3"; 101 + 102 + package acme.test.test; 103 + 104 + // Override the short name only of this package. This notation is seen in some 105 + // gogoproto files. 106 + option go_package = "test_test" 107 + 108 + message AnotherTest { 109 + int32 test = 1; 110 + } 111 + 112 + -- include/google/protobuf/timestamp.proto -- 113 + // Protocol Buffers - Google's data interchange format 114 + // Copyright 2008 Google Inc. All rights reserved. 115 + // https://developers.google.com/protocol-buffers/ 116 + // 117 + // Redistribution and use in source and binary forms, with or without 118 + // modification, are permitted provided that the following conditions are 119 + // met: 120 + // 121 + // * Redistributions of source code must retain the above copyright 122 + // notice, this list of conditions and the following disclaimer. 123 + // * Redistributions in binary form must reproduce the above 124 + // copyright notice, this list of conditions and the following disclaimer 125 + // in the documentation and/or other materials provided with the 126 + // distribution. 127 + // * Neither the name of Google Inc. nor the names of its 128 + // contributors may be used to endorse or promote products derived from 129 + // this software without specific prior written permission. 130 + // 131 + // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 132 + // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 133 + // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 134 + // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 135 + // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 136 + // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 137 + // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 138 + // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 139 + // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 140 + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 141 + // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 142 + 143 + syntax = "proto3"; 144 + 145 + package google.protobuf; 146 + 147 + option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 148 + option cc_enable_arenas = true; 149 + option go_package = "github.com/golang/protobuf/ptypes/timestamp"; 150 + option java_package = "com.google.protobuf"; 151 + option java_outer_classname = "TimestampProto"; 152 + option java_multiple_files = true; 153 + option objc_class_prefix = "GPB"; 154 + 155 + message Timestamp { 156 + // Represents seconds of UTC time since Unix epoch 157 + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to 158 + // 9999-12-31T23:59:59Z inclusive. 159 + int64 seconds = 1; 160 + 161 + // Non-negative fractions of a second at nanosecond resolution. Negative 162 + // second values with fractions must still have non-negative nanos values 163 + // that count forward in time. Must be from 0 to 999,999,999 164 + // inclusive. 165 + int32 nanos = 2; 166 + } 167 + 168 + -- expect-attributes_proto_gen.cue -- 169 + package v1 170 + 171 + import ( 172 + "time" 173 + "googleapis.com/acme/test" 174 + "googleapis.com/acme/test/test:test_test" 175 + ) 176 + 177 + // Attributes defines attributes. 178 + #Attributes: { 179 + #Type: {"TYPE_A", #enumValue: 1} | 180 + {"TYPE_B", #enumValue: 2} 181 + 182 + #Type_value: { 183 + TYPE_A: 1 184 + TYPE_B: 2 185 + } 186 + 187 + // A map of attribute name to its value. 188 + attributes?: { 189 + [string]: #AttributeValue 190 + } @protobuf(1,map[string]AttributeValue) 191 + 192 + // Specifies one attribute value with different type. 193 + #AttributeValue: { 194 + // The attribute value. 195 + {} | { 196 + stringValue: string @protobuf(2,string,name=string_value) 197 + } | { 198 + int64Value: int64 @protobuf(3,int64,name=int64_value) 199 + } | { 200 + doubleValue: float64 @protobuf(4,double,name=double_value) 201 + } | { 202 + boolValue: bool @protobuf(5,bool,name=bool_value) 203 + } | { 204 + bytesValue: bytes @protobuf(6,bytes,name=bytes_value) 205 + } | { 206 + timestampValue: time.Time @protobuf(7,google.protobuf.Timestamp,name=timestamp_value) 207 + } | { 208 + // Used for values of type STRING_MAP 209 + stringMapValue: #StringMap @protobuf(9,StringMap,name=string_map_value) 210 + } | { 211 + testValue: test.#Test @protobuf(10,acme.test.Test,name=test_value) 212 + } | { 213 + testValue: test_test.#AnotherTest @protobuf(11,acme.test.test.AnotherTest,name=test_value) 214 + } 215 + } 216 + 217 + // Defines a string map. 218 + #StringMap: { 219 + // Holds a set of name/value pairs. 220 + entries?: { 221 + [string]: string 222 + } @protobuf(1,map[string]string) 223 + } 224 + } 225 + -- expect-client_config_proto_gen.cue -- 226 + // Describes the configuration state for the Mixer client library that's built into Envoy. 227 + package client 228 + 229 + import "acme.com/api/mixer/v1" 230 + 231 + // Defines the per-service client configuration. 232 + #ServiceConfig: { 233 + disableCheckCalls?: bool @protobuf(1,bool,name=disable_check_calls) 234 + disableReportCalls?: bool @protobuf(2,bool,name=disable_report_calls) 235 + mixerAttributes?: v1.#Attributes @protobuf(3,Attributes,name=mixer_attributes) 236 + } 237 + -- expect-test_proto_gen.cue -- 238 + package test 239 + 240 + #Test: { 241 + test?: int32 @protobuf(1,int32) 242 + }
+6 -1
cue/path.go
··· 44 44 return sel.sel.kind() == adt.StringLabel 45 45 } 46 46 47 + // IsDefinition reports whether sel is a non-hidden definition label type. 48 + func (sel Selector) IsDefinition() bool { 49 + return sel.sel.kind() == adt.DefinitionLabel 50 + } 51 + 47 52 var ( 48 53 // AnyField can be used to ask for any single label. 49 54 // ··· 463 468 errors.Error 464 469 } 465 470 466 - func (p pathError) String() string { return p.Error.Error() } 471 + func (p pathError) String() string { return "" } 467 472 func (p pathError) optional() bool { return false } 468 473 func (p pathError) kind() adt.FeatureType { return 0 } 469 474 func (p pathError) feature(r adt.Runtime) adt.Feature {
+20 -12
encoding/protobuf/jsonpb/decoder.go
··· 24 24 "cuelang.org/go/cue/errors" 25 25 "cuelang.org/go/cue/literal" 26 26 "cuelang.org/go/cue/token" 27 + "cuelang.org/go/encoding/protobuf/pbinternal" 27 28 "github.com/cockroachdb/apd/v2" 28 29 ) 29 30 ··· 48 49 // float: string values are interpreted as numbers, and the values "NaN", 49 50 // "Infinity", and "-Infinity" are allowed and converted to 50 51 // to corresponding error values. 51 - // disjunction of strings: 52 - // this is assumed to represent a protobuf enum value. Strings 53 - // are left as is. For integers, the disjunction is resolved 54 - // by converting it to the string that has a corresponding #intValue 55 - // value. 52 + // enums: if a field is of type int and does not have a standard integer 53 + // type for its @protobuf attribute, this is assumed to represent 54 + // a protobuf enum value. Enum names are converted to integers 55 + // by interpreting the definitions of the disjunction constants 56 + // as the symbol names. 57 + // If CUE uses the string representation for enums, then an 58 + // #enumValue integer associated with the string value is used 59 + // for the conversion. 56 60 // {}: JSON objects representing any values will be left as is. 57 61 // If the CUE type corresponding to the URL can be determined within 58 62 // the module context it will be unified. ··· 133 137 field.Value = r.rewrite(v, field.Value) 134 138 } 135 139 } 140 + 141 + var enumValuePath = cue.ParsePath("#enumValue").Optional() 136 142 137 143 func (r *rewriter) rewrite(schema cue.Value, expr ast.Expr) (x ast.Expr) { 138 144 defer func() { ··· 189 195 } 190 196 191 197 var info literal.NumInfo 192 - if err := literal.ParseNum(str, &info); err != nil { 198 + if err := literal.ParseNum(str, &info); err == nil { 199 + x.Value = str 200 + x.Kind = token.FLOAT 201 + if info.IsInt() { 202 + x.Kind = token.INT 203 + } 193 204 break 194 205 } 195 - x.Value = str 196 - x.Kind = token.FLOAT 197 - if info.IsInt() { 198 - x.Kind = token.INT 199 - } 206 + 207 + pbinternal.MatchBySymbol(schema, str, x) 200 208 201 209 case cue.BytesKind: 202 210 x, q, str := stringValue(expr) ··· 245 253 values = []cue.Value{schema} // allow single values. 246 254 } 247 255 for _, v := range values { 248 - i, err := v.LookupPath(cue.MakePath(cue.Def("#intValue"))).Int64() 256 + i, err := v.LookupPath(enumValuePath).Int64() 249 257 if err == nil && i == enum { 250 258 str, err := v.String() 251 259 if err != nil {
+52 -10
encoding/protobuf/jsonpb/testdata/decoder/enums.txtar
··· 1 1 -- schema.cue -- 2 - enum: [string]: { 3 - "foo" 4 - #intValue: 1 5 - } | { 6 - "bar" 7 - #intValue: 2 8 - } 2 + enum: [string]: 3 + { "foo", #enumValue: 1 } | 4 + { "bar", #enumValue: 2 } 5 + 6 + defEnum: [string]: #foo | #bar 7 + 8 + #foo: 1 9 + #bar: 2 10 + 11 + typeEnum: [string]: #Enum 12 + 13 + #Enum: #foo | #bar 14 + 15 + 16 + // TODO: consider supporting @symbol(foo) or @json(,symbol=foo) 17 + // symbolEnum: [string]: 18 + // { 1, @symbol(foo) } | 19 + // { 2, @symbol(bar) } 20 + 9 21 10 - singleEnum: { "single", #intValue: 1 } 22 + singleEnum: { "single", #enumValue: 1 } 11 23 12 - badEnum: { string, #intValue: 1 } | { "two", #intValue: 2 } 24 + badEnum: { string, #enumValue: 1 } | { "two", #enumValue: 2 } 13 25 14 26 -- data.cue -- 15 27 enum: asIs: "foo" 16 28 enum: asIsUnknown: "foobar" 17 29 30 + // Convert integers to string 18 31 enum: numExistFoo: 1 19 32 enum: numExistBar: 2 20 33 34 + // Convert strings to integer 35 + defEnum: foo: "foo" 36 + defEnum: bar: "bar" 37 + defEnum: baz: "baz" // unavailable 38 + 39 + typeEnum: foo: "foo" 40 + typeEnum: bar: "bar" 41 + typeEnum: baz: "baz" // unavailable 42 + 43 + // TODO: consider supporting @symbol(foo) or @json(,symbol=foo) 44 + // symbolEnum: foo: "foo" 45 + // symbolEnum: bar: "bar" 46 + // symbolEnum: baz: "baz" 47 + 21 48 singleEnum: 1 22 49 23 50 -- errors.cue -- ··· 33 60 enum: asIs: "foo" 34 61 enum: asIsUnknown: "foobar" 35 62 63 + // Convert integers to string 36 64 enum: numExistFoo: "foo" 37 65 enum: numExistBar: "bar" 38 66 67 + // Convert strings to integer 68 + defEnum: foo: 1 69 + defEnum: bar: 2 70 + defEnum: baz: "baz" // unavailable 71 + 72 + typeEnum: foo: 1 73 + typeEnum: bar: 2 74 + typeEnum: baz: "baz" // unavailable 75 + 76 + // TODO: consider supporting @symbol(foo) or @json(,symbol=foo) 77 + // symbolEnum: foo: "foo" 78 + // symbolEnum: bar: "bar" 79 + // symbolEnum: baz: "baz" 80 + 39 81 singleEnum: "single" 40 82 -- out/jsonpb/errors.cue -- 41 83 enum.numNotExists: could not locate integer enum value 3: ··· 45 87 enum.tooLarge: invalid enum index: 4111222333444555666777888999: greater than max int64: 46 88 errors.cue:5:17 47 89 badEnum: invalid string enum: non-concrete value string: 48 - schema.cue:11:10 90 + schema.cue:23:10
+54 -20
encoding/protobuf/parse.go
··· 596 596 // Top-level enum entry. 597 597 enum := &ast.Field{Label: name} 598 598 addComments(enum, 1, x.Comment, nil) 599 + if p.current != nil && len(p.current.Elts) > 0 { 600 + ast.SetRelPos(enum, token.NewSection) 601 + } 599 602 600 603 // Top-level enum values entry. 601 604 valueName := ast.NewIdent(name.Name + "_value") ··· 608 611 panic(name.Name) 609 612 } 610 613 p.addDecl(enum) 611 - p.addDecl(d) 612 614 613 615 numEnums := 0 614 616 for _, v := range x.Elements { ··· 617 619 } 618 620 } 619 621 622 + lastSingle := false 623 + 624 + firstSpace := token.NewSection 625 + 620 626 // The line comments for an enum field need to attach after the '|', which 621 627 // is only known at the next iteration. 622 628 var lastComment *proto.Comment ··· 624 630 switch y := v.(type) { 625 631 case *proto.EnumField: 626 632 // Add enum value to map 633 + intValue := ast.NewLit(token.INT, strconv.Itoa(y.Integer)) 627 634 f := &ast.Field{ 628 635 Label: p.stringLit(y.Position, y.Name), 629 - Value: ast.NewLit(token.INT, strconv.Itoa(y.Integer)), 636 + Value: intValue, 630 637 } 631 638 valueMap.Elts = append(valueMap.Elts, f) 632 639 633 - // add to enum disjunction 634 - value := p.stringLit(y.Position, y.Name) 640 + var e ast.Expr 641 + switch p.state.enumMode { 642 + case "int": 643 + e = ast.NewIdent("#" + y.Name) 644 + ast.SetRelPos(e, token.Newline) 645 + 646 + f := &ast.Field{ 647 + Label: ast.NewIdent("#" + y.Name), 648 + Value: intValue, 649 + } 650 + ast.SetRelPos(f, firstSpace) 651 + firstSpace = token.Newline 652 + addComments(f, 0, y.Comment, y.InlineComment) 653 + p.addDecl(f) 635 654 636 - var e ast.Expr = value 637 - // Make the first value the default value. 638 - if i > 0 { 639 - value.ValuePos = newline 655 + case "", "json": 656 + // add to enum disjunction 657 + value := p.stringLit(y.Position, y.Name) 658 + embed := &ast.EmbedDecl{Expr: value} 659 + ast.SetRelPos(embed, token.Blank) 660 + field := &ast.Field{Label: ast.NewIdent("#enumValue"), Value: intValue} 661 + st := &ast.StructLit{ 662 + Lbrace: token.Blank.Pos(), 663 + Elts: []ast.Decl{embed, field}, 664 + } 665 + 666 + addComments(embed, 0, y.Comment, y.InlineComment) 667 + if y.Comment == nil && y.InlineComment == nil { 668 + ast.SetRelPos(field, token.Blank) 669 + ast.SetRelPos(field.Label, token.Blank) 670 + st.Rbrace = token.Blank.Pos() 671 + if i > 0 && lastSingle { 672 + st.Lbrace = token.Newline.Pos() 673 + } 674 + lastSingle = true 675 + } else { 676 + lastSingle = false 677 + } 678 + e = st 679 + 680 + default: 681 + p.state.errs = errors.Append(p.state.errs, 682 + errors.Newf(token.NoPos, "unknown enum mode %q", p.state.enumMode)) 683 + return 640 684 } 641 - addComments(e, i, y.Comment, nil) 685 + 642 686 if enum.Value != nil { 643 687 e = &ast.BinaryExpr{X: enum.Value, Op: token.OR, Y: e} 644 - if cg := comment(lastComment, false); cg != nil { 645 - cg.Position = 2 646 - e.AddComment(cg) 647 - } 648 688 } 649 689 enum.Value = e 650 690 651 - if y.Comment != nil { 652 - lastComment = nil 653 - addComments(f, 0, nil, y.InlineComment) 654 - } else { 655 - lastComment = y.InlineComment 656 - } 657 - 658 691 // a := fmt.Sprintf("@protobuf(enum,name=%s)", y.Name) 659 692 // f.Attrs = append(f.Attrs, &ast.Attribute{Text: a}) 660 693 } 661 694 } 695 + p.addDecl(d) 662 696 addComments(enum.Value, 1, nil, lastComment) 663 697 } 664 698
+66
encoding/protobuf/pbinternal/symbol.go
··· 1 + // Copyright 2021 CUE Authors 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 + // http://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 pbinternal 16 + 17 + import ( 18 + "strconv" 19 + 20 + "cuelang.org/go/cue" 21 + "cuelang.org/go/cue/ast" 22 + "cuelang.org/go/cue/token" 23 + ) 24 + 25 + // MatchBySymbol finds an integer value for a given symbol name, representing 26 + // an enum value, and sets it in x. 27 + func MatchBySymbol(v cue.Value, name string, x *ast.BasicLit) bool { 28 + if op, a := v.Expr(); op == cue.AndOp { 29 + for _, v := range a { 30 + if MatchBySymbol(v, name, x) { 31 + return true 32 + } 33 + } 34 + } 35 + return matchBySymbol(cue.Dereference(v), name, x) 36 + } 37 + 38 + func matchBySymbol(v cue.Value, name string, x *ast.BasicLit) bool { 39 + switch op, a := v.Expr(); op { 40 + case cue.OrOp, cue.AndOp: 41 + for _, v := range a { 42 + if matchBySymbol(v, name, x) { 43 + return true 44 + } 45 + } 46 + 47 + default: 48 + _, path := v.ReferencePath() 49 + 50 + a := path.Selectors() 51 + if len(a) == 0 { 52 + break 53 + } 54 + if s := a[len(a)-1]; !s.IsDefinition() || s.String()[1:] != name { 55 + break 56 + } 57 + 58 + if i, err := v.Int64(); err == nil { 59 + x.Kind = token.INT 60 + x.Value = strconv.Itoa(int(i)) 61 + return true 62 + } 63 + } 64 + 65 + return false 66 + }
+21 -5
encoding/protobuf/protobuf.go
··· 133 133 // PkgName specifies the package name for a generated CUE file. A value 134 134 // will be derived from the Go package name if undefined. 135 135 PkgName string 136 + 137 + // EnumMode defines whether enums should be set as integer values, instead 138 + // of strings. 139 + // 140 + // json value is a string, corresponding to the standard JSON mapping 141 + // of Protobuf. The value is associated with a #enumValue 142 + // to allow the json+pb interpretation to interpret integers 143 + // as well. 144 + // 145 + // int value is an integer associated with an #enumValue definition 146 + // The json+pb interpreter uses the definition names in the 147 + // disjunction of the enum to interpret strings. 148 + // 149 + EnumMode string 136 150 } 137 151 138 152 // An Extractor converts a collection of proto files, typically belonging to one ··· 146 160 // according to their Go package import path. 147 161 // 148 162 type Extractor struct { 149 - root string 150 - cwd string 151 - module string 152 - paths []string 153 - pkgName string 163 + root string 164 + cwd string 165 + module string 166 + paths []string 167 + pkgName string 168 + enumMode string 154 169 155 170 fileCache map[string]result 156 171 imports map[string]*build.Instance ··· 175 190 paths: c.Paths, 176 191 pkgName: c.PkgName, 177 192 module: c.Module, 193 + enumMode: c.EnumMode, 178 194 fileCache: map[string]result{}, 179 195 imports: map[string]*build.Instance{}, 180 196 }
+3 -1
encoding/protobuf/testdata/client_config.proto.out.cue
··· 27 27 // Specifies the behavior when the client is unable to connect to Mixer. 28 28 #NetworkFailPolicy: { 29 29 // Example of single-value enum. 30 - #FailPolicy: 30 + #FailPolicy: { 31 31 // If network connection fails, request is allowed and delivered to the 32 32 // service. 33 33 "FAIL_OPEN" 34 + #enumValue: 0 35 + } 34 36 35 37 #FailPolicy_value: FAIL_OPEN: 0 36 38
+28 -12
encoding/protobuf/testdata/gateway.proto.out.cue
··· 324 324 httpsRedirect?: bool @protobuf(1,bool,name=https_redirect) 325 325 326 326 // TLS modes enforced by the proxy 327 - #TLSmode: 327 + #TLSmode: { 328 328 // The SNI string presented by the client will be used as the match 329 329 // criterion in a VirtualService TLS route to determine the 330 330 // destination service from the service registry. 331 - "PASSTHROUGH" | 332 - 331 + "PASSTHROUGH" 332 + #enumValue: 0 333 + } | { 333 334 // Secure connections with standard TLS semantics. 334 - "SIMPLE" | 335 - 335 + "SIMPLE" 336 + #enumValue: 1 337 + } | { 336 338 // Secure connections to the upstream using mutual TLS by presenting 337 339 // client certificates for authentication. 338 - "MUTUAL" | 339 - 340 + "MUTUAL" 341 + #enumValue: 2 342 + } | { 340 343 // Similar to the passthrough mode, except servers with this TLS mode 341 344 // do not require an associated VirtualService to map from the SNI 342 345 // value to service in the registry. The destination details such as ··· 348 351 // their respective endpoints. Use of this mode assumes that both the 349 352 // source and the destination are using Istio mTLS to secure traffic. 350 353 "AUTO_PASSTHROUGH" 354 + #enumValue: 3 355 + } 351 356 352 357 #TLSmode_value: { 353 358 PASSTHROUGH: 0 ··· 398 403 subjectAltNames?: [...string] @protobuf(6,string,name=subject_alt_names) 399 404 400 405 // TLS protocol versions. 401 - #TLSProtocol: "TLS_AUTO" | // Automatically choose the optimal TLS version. 402 - "TLSV1_0" | // TLS version 1.0 403 - "TLSV1_1" | // TLS version 1.1 404 - "TLSV1_2" | // TLS version 1.2 405 - "TLSV1_3" // TLS version 1.3 406 + #TLSProtocol: { 407 + "TLS_AUTO"// Automatically choose the optimal TLS version. 408 + #enumValue: 0 409 + } | { 410 + "TLSV1_0"// TLS version 1.0 411 + #enumValue: 1 412 + } | { 413 + "TLSV1_1"// TLS version 1.1 414 + #enumValue: 2 415 + } | { 416 + "TLSV1_2"// TLS version 1.2 417 + #enumValue: 3 418 + } | { 419 + "TLSV1_3"// TLS version 1.3 420 + #enumValue: 4 421 + } 406 422 407 423 #TLSProtocol_value: { 408 424 TLS_AUTO: 0
+76 -49
encoding/protobuf/testdata/istio.io/api/cue.mod/gen/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor_proto_gen.cue
··· 149 149 150 150 // Describes a field within a message. 151 151 #FieldDescriptorProto: { 152 - #Type: 152 + #Type: { 153 153 // 0 is reserved for errors. 154 154 // Order is weird for historical reasons. 155 - "TYPE_DOUBLE" | 156 - "TYPE_FLOAT" | 157 - 155 + "TYPE_DOUBLE" 156 + #enumValue: 1 157 + } | {"TYPE_FLOAT", #enumValue: 2} | { 158 158 // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if 159 159 // negative values are likely. 160 - "TYPE_INT64" | 161 - "TYPE_UINT64" | 162 - 160 + "TYPE_INT64" 161 + #enumValue: 3 162 + } | {"TYPE_UINT64", #enumValue: 4} | { 163 163 // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if 164 164 // negative values are likely. 165 - "TYPE_INT32" | 166 - "TYPE_FIXED64" | 167 - "TYPE_FIXED32" | 168 - "TYPE_BOOL" | 169 - "TYPE_STRING" | 170 - 171 - // Tag-delimited aggregate. 172 - // Group type is deprecated and not supported in proto3. However, Proto3 173 - // implementations should still be able to parse the group wire format and 174 - // treat group fields as unknown fields. 175 - "TYPE_GROUP" | 176 - "TYPE_MESSAGE" | // Length-delimited aggregate. 177 - 178 - // New in version 2. 179 - "TYPE_BYTES" | 180 - "TYPE_UINT32" | 181 - "TYPE_ENUM" | 182 - "TYPE_SFIXED32" | 183 - "TYPE_SFIXED64" | 184 - "TYPE_SINT32" | // Uses ZigZag encoding. 185 - "TYPE_SINT64" // Uses ZigZag encoding. 165 + "TYPE_INT32" 166 + #enumValue: 5 167 + } | {"TYPE_FIXED64", #enumValue: 6} | 168 + {"TYPE_FIXED32", #enumValue: 7} | 169 + {"TYPE_BOOL", #enumValue: 8} | 170 + {"TYPE_STRING", #enumValue: 9} | { 171 + // Tag-delimited aggregate. 172 + // Group type is deprecated and not supported in proto3. However, Proto3 173 + // implementations should still be able to parse the group wire format and 174 + // treat group fields as unknown fields. 175 + "TYPE_GROUP" 176 + #enumValue: 10 177 + } | { 178 + "TYPE_MESSAGE"// Length-delimited aggregate. 179 + #enumValue: 11 180 + } | { 181 + // New in version 2. 182 + "TYPE_BYTES" 183 + #enumValue: 12 184 + } | {"TYPE_UINT32", #enumValue: 13} | 185 + {"TYPE_ENUM", #enumValue: 14} | 186 + {"TYPE_SFIXED32", #enumValue: 15} | 187 + {"TYPE_SFIXED64", #enumValue: 16} | { 188 + "TYPE_SINT32"// Uses ZigZag encoding. 189 + #enumValue: 17 190 + } | { 191 + "TYPE_SINT64"// Uses ZigZag encoding. 192 + #enumValue: 18 193 + } 186 194 187 195 #Type_value: { 188 196 "TYPE_DOUBLE": 1 ··· 204 212 "TYPE_SINT32": 17 205 213 "TYPE_SINT64": 18 206 214 } 207 - #Label: 215 + 216 + #Label: { 208 217 // 0 is reserved for errors 209 - "LABEL_OPTIONAL" | 210 - "LABEL_REQUIRED" | 211 - "LABEL_REPEATED" 218 + "LABEL_OPTIONAL" 219 + #enumValue: 1 220 + } | {"LABEL_REQUIRED", #enumValue: 2} | 221 + {"LABEL_REPEATED", #enumValue: 3} 212 222 213 223 #Label_value: { 214 224 "LABEL_OPTIONAL": 1 ··· 351 361 javaStringCheckUtf8?: bool @protobuf(27,bool,name=java_string_check_utf8,"default=false") 352 362 353 363 // Generated classes can be optimized for speed or code size. 354 - #OptimizeMode: "SPEED" | // Generate complete code for parsing, serialization, 355 - 364 + #OptimizeMode: { 365 + "SPEED"// Generate complete code for parsing, serialization, 366 + #enumValue: 1 367 + } | { 356 368 // etc. 357 - "CODE_SIZE" | 358 - "LITE_RUNTIME" // Generate code using MessageLite and the lite runtime. 369 + "CODE_SIZE"// Use ReflectionOps to implement these methods. 370 + #enumValue: 2 371 + } | { 372 + "LITE_RUNTIME"// Generate code using MessageLite and the lite runtime. 373 + #enumValue: 3 374 + } 359 375 360 376 #OptimizeMode_value: { 361 377 "SPEED": 1 362 - "CODE_SIZE": 2 // Use ReflectionOps to implement these methods. 378 + "CODE_SIZE": 2 363 379 "LITE_RUNTIME": 3 364 380 } 365 381 optimizeFor?: #OptimizeMode @protobuf(9,OptimizeMode,name=optimize_for,"default=SPEED") ··· 498 514 // options below. This option is not yet implemented in the open source 499 515 // release -- sorry, we'll try to include it in a future version! 500 516 ctype?: #CType @protobuf(1,CType,"default=STRING") 501 - #CType: 517 + 518 + #CType: { 502 519 // Default mode. 503 - "STRING" | 504 - "CORD" | 505 - "STRING_PIECE" 520 + "STRING" 521 + #enumValue: 0 522 + } | {"CORD", #enumValue: 1} | 523 + {"STRING_PIECE", #enumValue: 2} 506 524 507 525 #CType_value: { 508 526 "STRING": 0 ··· 529 547 // This option is an enum to permit additional types to be added, e.g. 530 548 // goog.math.Integer. 531 549 jstype?: #JSType @protobuf(6,JSType,"default=JS_NORMAL") 532 - #JSType: 533 - // Use the default type. 534 - "JS_NORMAL" | 535 550 551 + #JSType: { 552 + // Use the default type. 553 + "JS_NORMAL" 554 + #enumValue: 0 555 + } | { 536 556 // Use JavaScript strings. 537 - "JS_STRING" | 538 - 557 + "JS_STRING" 558 + #enumValue: 1 559 + } | { 539 560 // Use JavaScript numbers. 540 561 "JS_NUMBER" 562 + #enumValue: 2 563 + } 541 564 542 565 #JSType_value: { 543 566 "JS_NORMAL": 0 ··· 650 673 // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, 651 674 // or neither? HTTP based RPC implementation may choose GET verb for safe 652 675 // methods, and PUT verb for idempotent methods instead of the default POST. 653 - #IdempotencyLevel: "IDEMPOTENCY_UNKNOWN" | 654 - "NO_SIDE_EFFECTS" | // implies idempotent 655 - "IDEMPOTENT" // idempotent, but may have side effects 676 + #IdempotencyLevel: {"IDEMPOTENCY_UNKNOWN", #enumValue: 0} | { 677 + "NO_SIDE_EFFECTS"// implies idempotent 678 + #enumValue: 1 679 + } | { 680 + "IDEMPOTENT"// idempotent, but may have side effects 681 + #enumValue: 2 682 + } 656 683 657 684 #IdempotencyLevel_value: { 658 685 "IDEMPOTENCY_UNKNOWN": 0
+3 -1
encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue
··· 27 27 // Specifies the behavior when the client is unable to connect to Mixer. 28 28 #NetworkFailPolicy: { 29 29 // Example of single-value enum. 30 - #FailPolicy: 30 + #FailPolicy: { 31 31 // If network connection fails, request is allowed and delivered to the 32 32 // service. 33 33 "FAIL_OPEN" 34 + #enumValue: 0 35 + } 34 36 35 37 #FailPolicy_value: "FAIL_OPEN": 0 36 38
+29 -10
encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue
··· 103 103 // This can be used to construct a response cache. 104 104 #ReferencedAttributes: { 105 105 // How an attribute's value was matched 106 - #Condition: "CONDITION_UNSPECIFIED" | // should not occur 107 - "ABSENCE" | // match when attribute doesn't exist 108 - "EXACT" | // match when attribute value is an exact byte-for-byte match 109 - "REGEX" // match when attribute value matches the included regex 106 + #Condition: { 107 + "CONDITION_UNSPECIFIED"// should not occur 108 + #enumValue: 0 109 + } | { 110 + "ABSENCE"// match when attribute doesn't exist 111 + #enumValue: 1 112 + } | { 113 + "EXACT"// match when attribute value is an exact byte-for-byte match 114 + #enumValue: 2 115 + } | { 116 + "REGEX"// match when attribute value matches the included regex 117 + #enumValue: 3 118 + } 110 119 111 120 #Condition_value: { 112 121 "CONDITION_UNSPECIFIED": 0 ··· 155 164 // the request headers. 156 165 #HeaderOperation: { 157 166 // Operation type. 158 - #Operation: "REPLACE" | // replaces the header with the given name 159 - "REMOVE" | // removes the header with the given name (the value is ignored) 160 - "APPEND" // appends the value to the header value, or sets it if not present 167 + #Operation: { 168 + "REPLACE"// replaces the header with the given name 169 + #enumValue: 0 170 + } | { 171 + "REMOVE"// removes the header with the given name (the value is ignored) 172 + #enumValue: 1 173 + } | { 174 + "APPEND"// appends the value to the header value, or sets it if not present 175 + #enumValue: 2 176 + } 161 177 162 178 #Operation_value: { 163 179 "REPLACE": 0 ··· 198 214 // next value: 5 199 215 200 216 // Used to signal how the sets of compressed attributes should be reconstitued server-side. 201 - #RepeatedAttributesSemantics: 217 + #RepeatedAttributesSemantics: { 202 218 // Use delta encoding between sets of compressed attributes to reduce the overall on-wire 203 219 // request size. Each individual set of attributes is used to modify the previous set. 204 220 // NOTE: There is no way with this encoding to specify attribute value deletion. This 205 221 // option should be used with extreme caution. 206 - "DELTA_ENCODING" | 207 - 222 + "DELTA_ENCODING" 223 + #enumValue: 0 224 + } | { 208 225 // Treat each set of compressed attributes as complete - independent from other sets 209 226 // in this request. This will result in on-wire duplication of attributes and values, but 210 227 // will allow for proper accounting of absent values in overall encoding. 211 228 "INDEPENDENT_ENCODING" 229 + #enumValue: 1 230 + } 212 231 213 232 #RepeatedAttributesSemantics_value: { 214 233 "DELTA_ENCODING": 0
+28 -12
encoding/protobuf/testdata/istio.io/api/networking/v1alpha3/gateway_proto_gen.cue
··· 324 324 httpsRedirect?: bool @protobuf(1,bool,name=https_redirect) 325 325 326 326 // TLS modes enforced by the proxy 327 - #TLSmode: 327 + #TLSmode: { 328 328 // The SNI string presented by the client will be used as the match 329 329 // criterion in a VirtualService TLS route to determine the 330 330 // destination service from the service registry. 331 - "PASSTHROUGH" | 332 - 331 + "PASSTHROUGH" 332 + #enumValue: 0 333 + } | { 333 334 // Secure connections with standard TLS semantics. 334 - "SIMPLE" | 335 - 335 + "SIMPLE" 336 + #enumValue: 1 337 + } | { 336 338 // Secure connections to the upstream using mutual TLS by presenting 337 339 // client certificates for authentication. 338 - "MUTUAL" | 339 - 340 + "MUTUAL" 341 + #enumValue: 2 342 + } | { 340 343 // Similar to the passthrough mode, except servers with this TLS mode 341 344 // do not require an associated VirtualService to map from the SNI 342 345 // value to service in the registry. The destination details such as ··· 348 351 // their respective endpoints. Use of this mode assumes that both the 349 352 // source and the destination are using Istio mTLS to secure traffic. 350 353 "AUTO_PASSTHROUGH" 354 + #enumValue: 3 355 + } 351 356 352 357 #TLSmode_value: { 353 358 "PASSTHROUGH": 0 ··· 398 403 subjectAltNames?: [...string] @protobuf(6,string,name=subject_alt_names) 399 404 400 405 // TLS protocol versions. 401 - #TLSProtocol: "TLS_AUTO" | // Automatically choose the optimal TLS version. 402 - "TLSV1_0" | // TLS version 1.0 403 - "TLSV1_1" | // TLS version 1.1 404 - "TLSV1_2" | // TLS version 1.2 405 - "TLSV1_3" // TLS version 1.3 406 + #TLSProtocol: { 407 + "TLS_AUTO"// Automatically choose the optimal TLS version. 408 + #enumValue: 0 409 + } | { 410 + "TLSV1_0"// TLS version 1.0 411 + #enumValue: 1 412 + } | { 413 + "TLSV1_1"// TLS version 1.1 414 + #enumValue: 2 415 + } | { 416 + "TLSV1_2"// TLS version 1.2 417 + #enumValue: 3 418 + } | { 419 + "TLSV1_3"// TLS version 1.3 420 + #enumValue: 4 421 + } 406 422 407 423 #TLSProtocol_value: { 408 424 "TLS_AUTO": 0