encoding/protobuf: define messages as closed

This solves the issue that the only way to map a
Protocol Buffer Oneof to CUE is to by using closed
structs.

This may seem counterintuitive, as the Protocol spec
defines all messages as extensible. There is nothing
to prevent the extension of a CUE defintion, however,
in that sense. Closedness exists to type check within
a single version of an API.

Mapping to closed structs is analogous to a generated
Go spec from Protobufs not allowing arbitrary fields.

encoding/openapi is adjusted accordingly.

This solves a mapping issue related from Istio.

Issue istio/istio#22100.

Change-Id: I69196de5d39e18720e151e04ef59c789d9424aa6
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5321
Reviewed-by: Jason Wang <jasonwzm@google.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/testdata/script/def_proto.txt b/cmd/cue/cmd/testdata/script/def_proto.txt
index 59fc070..7708745 100644
--- a/cmd/cue/cmd/testdata/script/def_proto.txt
+++ b/cmd/cue/cmd/testdata/script/def_proto.txt
@@ -8,7 +8,7 @@
 import "time"
 
 // Attributes defines attributes.
-Attributes: {
+Attributes :: {
 	// A map of attribute name to its value.
 	attributes?: {
 		[string]: Attributes_AttributeValue
@@ -16,10 +16,11 @@
 }
 
 // Specifies one attribute value with different type.
-Attributes_AttributeValue: {
+Attributes_AttributeValue :: {
 }
+
 // The attribute value.
-Attributes_AttributeValue: {
+Attributes_AttributeValue :: {
 	stringValue: string @protobuf(2,name=string_value)
 } | {
 	int64Value: int64 @protobuf(3,name=int64_value)
@@ -37,7 +38,7 @@
 }
 
 // Defines a string map.
-Attributes_StringMap: {
+Attributes_StringMap :: {
 	// Holds a set of name/value pairs.
 	entries?: {
 		[string]: string
diff --git a/encoding/jsonschema/constraints.go b/encoding/jsonschema/constraints.go
index 4b0cd32..b7d4335 100644
--- a/encoding/jsonschema/constraints.go
+++ b/encoding/jsonschema/constraints.go
@@ -232,7 +232,10 @@
 	}),
 
 	p0("oneOf", func(n cue.Value, s *state) {
-		combineSequence("allOf", n, s, token.OR, s.schema)
+		combineSequence("oneOf", n, s, token.OR, s.schema)
+
+		// TODO: oneOf({a:x}, {b:y}, ..., not(anyOf({a:x}, {b:y}, ...))),
+		// can be translated to {} | {a:x}, {b:y}, ...
 	}),
 
 	// String constraints
diff --git a/encoding/openapi/build.go b/encoding/openapi/build.go
index f75181f..c3c485d 100644
--- a/encoding/openapi/build.go
+++ b/encoding/openapi/build.go
@@ -487,24 +487,33 @@
 		anyOf = append(anyOf, b.kv("enum", ast.NewList(enums...)))
 	}
 
-	hasEmpty := false
+	hasAny := false
 	for _, v := range disjuncts {
 		c := newOASBuilder(b)
 		c.value(v, f)
 		t := c.finish()
 		if len(t.Elts) == 0 {
-			hasEmpty = true
+			if c.typ == "" {
+				hasAny = true
+			}
+			continue
 		}
 		anyOf = append(anyOf, (*ast.StructLit)(t))
 	}
 
 	// If any of the types was "any", a oneOf may be discarded.
-	if !hasEmpty {
-		b.set("oneOf", ast.NewList(anyOf...))
+	if !hasAny {
+		// TODO: analyze CUE structs to figure out if it should be oneOf or
+		// anyOf. More precisely, if non of the elements subsume each other,
+		// it is oneOf, if some do, it is anyOf. For closed structs, the
+		// structure needs to be oneOf(X, not(anyOf(X))).
+		// As the source is protobuf for now, it is always the latter form.
+		b.set("oneOf", ast.NewList(
+			append(anyOf,
+				ast.NewStruct("not",
+					ast.NewStruct("anyOf", ast.NewList(anyOf...))))...))
 	}
 
-	// TODO: analyze CUE structs to figure out if it should be oneOf or
-	// anyOf. As the source is protobuf for now, it is always oneOf.
 	if nullable {
 		b.setSingle("nullable", ast.NewBool(true), true)
 	}
diff --git a/encoding/openapi/testdata/oneof-funcs.json b/encoding/openapi/testdata/oneof-funcs.json
index 104adcb..4e4da66 100644
--- a/encoding/openapi/testdata/oneof-funcs.json
+++ b/encoding/openapi/testdata/oneof-funcs.json
@@ -34,6 +34,36 @@
                         "format": "string"
                      }
                   }
+               },
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "exact"
+                           ],
+                           "properties": {
+                              "exact": {
+                                 "description": "Randomly picked description from a set of size one.",
+                                 "type": "string",
+                                 "format": "string"
+                              }
+                           }
+                        },
+                        {
+                           "required": [
+                              "regex"
+                           ],
+                           "properties": {
+                              "regex": {
+                                 "description": "Randomly picked description from a set of size one.",
+                                 "type": "string",
+                                 "format": "string"
+                              }
+                           }
+                        }
+                     ]
+                  }
                }
             ]
          },
diff --git a/encoding/openapi/testdata/oneof-resolve.json b/encoding/openapi/testdata/oneof-resolve.json
index 629db2f..62158c5 100644
--- a/encoding/openapi/testdata/oneof-resolve.json
+++ b/encoding/openapi/testdata/oneof-resolve.json
@@ -29,6 +29,22 @@
                   "required": [
                      "regex"
                   ]
+               },
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "exact"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "regex"
+                           ]
+                        }
+                     ]
+                  }
                }
             ]
          },
@@ -65,6 +81,22 @@
                         "required": [
                            "regex"
                         ]
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "exact"
+                                 ]
+                              },
+                              {
+                                 "required": [
+                                    "regex"
+                                 ]
+                              }
+                           ]
+                        }
                      }
                   ]
                },
@@ -92,6 +124,22 @@
                            "required": [
                               "regex"
                            ]
+                        },
+                        {
+                           "not": {
+                              "anyOf": [
+                                 {
+                                    "required": [
+                                       "exact"
+                                    ]
+                                 },
+                                 {
+                                    "required": [
+                                       "regex"
+                                    ]
+                                 }
+                              ]
+                           }
                         }
                      ]
                   }
diff --git a/encoding/openapi/testdata/oneof.cue b/encoding/openapi/testdata/oneof.cue
index 768b442..a7aad07 100644
--- a/encoding/openapi/testdata/oneof.cue
+++ b/encoding/openapi/testdata/oneof.cue
@@ -1,4 +1,4 @@
-MyString :: {
+MyString :: {} | {
 	exact: string
 } | {
 	regex: string
diff --git a/encoding/openapi/testdata/oneof.json b/encoding/openapi/testdata/oneof.json
index cb84e49..40b31b9 100644
--- a/encoding/openapi/testdata/oneof.json
+++ b/encoding/openapi/testdata/oneof.json
@@ -28,6 +28,34 @@
                         "format": "string"
                      }
                   }
+               },
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "exact"
+                           ],
+                           "properties": {
+                              "exact": {
+                                 "type": "string",
+                                 "format": "string"
+                              }
+                           }
+                        },
+                        {
+                           "required": [
+                              "regex"
+                           ],
+                           "properties": {
+                              "regex": {
+                                 "type": "string",
+                                 "format": "string"
+                              }
+                           }
+                        }
+                     ]
+                  }
                }
             ]
          },
diff --git a/encoding/openapi/testdata/openapi-norefs.json b/encoding/openapi/testdata/openapi-norefs.json
index dc246b1..e8c5fd0 100644
--- a/encoding/openapi/testdata/openapi-norefs.json
+++ b/encoding/openapi/testdata/openapi-norefs.json
@@ -67,6 +67,22 @@
                   "required": [
                      "b"
                   ]
+               },
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "a"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "b"
+                           ]
+                        }
+                     ]
+                  }
                }
             ]
          },
@@ -113,6 +129,22 @@
                   "required": [
                      "b"
                   ]
+               },
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "b"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "b"
+                           ]
+                        }
+                     ]
+                  }
                }
             ]
          },
@@ -150,6 +182,22 @@
                         "required": [
                            "b"
                         ]
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "a"
+                                 ]
+                              },
+                              {
+                                 "required": [
+                                    "b"
+                                 ]
+                              }
+                           ]
+                        }
                      }
                   ]
                },
@@ -164,6 +212,22 @@
                         "required": [
                            "d"
                         ]
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "c"
+                                 ]
+                              },
+                              {
+                                 "required": [
+                                    "d"
+                                 ]
+                              }
+                           ]
+                        }
                      }
                   ]
                },
@@ -178,6 +242,22 @@
                         "required": [
                            "f"
                         ]
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "e"
+                                 ]
+                              },
+                              {
+                                 "required": [
+                                    "f"
+                                 ]
+                              }
+                           ]
+                        }
                      }
                   ]
                }
@@ -204,6 +284,22 @@
                   "required": [
                      "a"
                   ]
+               },
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "b"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "a"
+                           ]
+                        }
+                     ]
+                  }
                }
             ]
          },
@@ -251,6 +347,23 @@
                   "required": [
                      "port"
                   ]
+               },
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "port",
+                              "obj"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "port"
+                           ]
+                        }
+                     ]
+                  }
                }
             ]
          }
diff --git a/encoding/openapi/testdata/openapi.json b/encoding/openapi/testdata/openapi.json
index 07dcba3..0438a36 100644
--- a/encoding/openapi/testdata/openapi.json
+++ b/encoding/openapi/testdata/openapi.json
@@ -61,6 +61,37 @@
                         "format": "string"
                      }
                   }
+               },
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "a"
+                           ],
+                           "properties": {
+                              "a": {
+                                 "description": "Field a.",
+                                 "type": "integer",
+                                 "enum": [
+                                    1
+                                 ]
+                              }
+                           }
+                        },
+                        {
+                           "required": [
+                              "b"
+                           ],
+                           "properties": {
+                              "b": {
+                                 "type": "string",
+                                 "format": "string"
+                              }
+                           }
+                        }
+                     ]
+                  }
                }
             ]
          },
@@ -117,6 +148,41 @@
                         "type": "number"
                      }
                   }
+               },
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "b"
+                           ],
+                           "properties": {
+                              "a": {
+                                 "type": "string",
+                                 "format": "string"
+                              },
+                              "b": {
+                                 "type": "string",
+                                 "format": "string"
+                              }
+                           }
+                        },
+                        {
+                           "required": [
+                              "b"
+                           ],
+                           "properties": {
+                              "a": {
+                                 "type": "string",
+                                 "format": "string"
+                              },
+                              "b": {
+                                 "type": "number"
+                              }
+                           }
+                        }
+                     ]
+                  }
                }
             ]
          },
@@ -144,6 +210,32 @@
                               "type": "number"
                            }
                         }
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "a"
+                                 ],
+                                 "properties": {
+                                    "a": {
+                                       "type": "number"
+                                    }
+                                 }
+                              },
+                              {
+                                 "required": [
+                                    "b"
+                                 ],
+                                 "properties": {
+                                    "b": {
+                                       "type": "number"
+                                    }
+                                 }
+                              }
+                           ]
+                        }
                      }
                   ]
                },
@@ -168,6 +260,32 @@
                               "type": "number"
                            }
                         }
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "c"
+                                 ],
+                                 "properties": {
+                                    "c": {
+                                       "type": "number"
+                                    }
+                                 }
+                              },
+                              {
+                                 "required": [
+                                    "d"
+                                 ],
+                                 "properties": {
+                                    "d": {
+                                       "type": "number"
+                                    }
+                                 }
+                              }
+                           ]
+                        }
                      }
                   ]
                },
@@ -192,6 +310,32 @@
                               "type": "number"
                            }
                         }
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "e"
+                                 ],
+                                 "properties": {
+                                    "e": {
+                                       "type": "number"
+                                    }
+                                 }
+                              },
+                              {
+                                 "required": [
+                                    "f"
+                                 ],
+                                 "properties": {
+                                    "f": {
+                                       "type": "number"
+                                    }
+                                 }
+                              }
+                           ]
+                        }
                      }
                   ]
                }
@@ -220,6 +364,33 @@
                         "format": "string"
                      }
                   }
+               },
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "b"
+                           ],
+                           "properties": {
+                              "b": {
+                                 "type": "number"
+                              }
+                           }
+                        },
+                        {
+                           "required": [
+                              "a"
+                           ],
+                           "properties": {
+                              "a": {
+                                 "type": "string",
+                                 "format": "string"
+                              }
+                           }
+                        }
+                     ]
+                  }
                }
             ]
          },
@@ -263,6 +434,28 @@
                         ]
                      }
                   }
+               },
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "$ref": "#/components/schemas/Port"
+                        },
+                        {
+                           "required": [
+                              "port"
+                           ],
+                           "properties": {
+                              "port": {
+                                 "type": "integer",
+                                 "enum": [
+                                    1
+                                 ]
+                              }
+                           }
+                        }
+                     ]
+                  }
                }
             ]
          }
diff --git a/encoding/openapi/testdata/structural.json b/encoding/openapi/testdata/structural.json
index dab5eb7..ad0b83a 100644
--- a/encoding/openapi/testdata/structural.json
+++ b/encoding/openapi/testdata/structural.json
@@ -110,6 +110,52 @@
                            "required": [
                               "stringMapValue"
                            ]
+                        },
+                        {
+                           "not": {
+                              "anyOf": [
+                                 {
+                                    "required": [
+                                       "stringValue"
+                                    ]
+                                 },
+                                 {
+                                    "required": [
+                                       "int64Value"
+                                    ]
+                                 },
+                                 {
+                                    "required": [
+                                       "doubleValue"
+                                    ]
+                                 },
+                                 {
+                                    "required": [
+                                       "boolValue"
+                                    ]
+                                 },
+                                 {
+                                    "required": [
+                                       "bytesValue"
+                                    ]
+                                 },
+                                 {
+                                    "required": [
+                                       "timestampValue"
+                                    ]
+                                 },
+                                 {
+                                    "required": [
+                                       "durationValue"
+                                    ]
+                                 },
+                                 {
+                                    "required": [
+                                       "stringMapValue"
+                                    ]
+                                 }
+                              ]
+                           }
                         }
                      ]
                   }
@@ -211,6 +257,52 @@
                   "required": [
                      "stringMapValue"
                   ]
+               },
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "stringValue"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "int64Value"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "doubleValue"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "boolValue"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "bytesValue"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "timestampValue"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "durationValue"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "stringMapValue"
+                           ]
+                        }
+                     ]
+                  }
                }
             ]
          },
diff --git a/encoding/protobuf/examples_test.go b/encoding/protobuf/examples_test.go
index 5d7a7c8..20370fc 100644
--- a/encoding/protobuf/examples_test.go
+++ b/encoding/protobuf/examples_test.go
@@ -46,7 +46,7 @@
 	// package basic
 	//
 	// // This is my type.
-	// MyType: {
+	// MyType :: {
 	// 	stringValue?: string @protobuf(1,name=string_value) // just any 'ole string
 	//
 	// 	// A method must start with a capital letter.
diff --git a/encoding/protobuf/parse.go b/encoding/protobuf/parse.go
index fa35d06..e77db1f 100644
--- a/encoding/protobuf/parse.go
+++ b/encoding/protobuf/parse.go
@@ -492,7 +492,7 @@
 	if v.Comment == nil {
 		ref.NamePos = newSection
 	}
-	f := &ast.Field{Label: ref, Value: s}
+	f := &ast.Field{Label: ref, Token: token.ISA, Value: s}
 	addComments(f, 1, v.Comment, nil)
 
 	p.file.Decls = append(p.file.Decls, f)
@@ -602,14 +602,14 @@
 	}
 
 	// Top-level enum entry.
-	enum := &ast.Field{Label: name}
+	enum := &ast.Field{Label: name, Token: token.ISA}
 	addComments(enum, 1, x.Comment, nil)
 
 	// Top-level enum values entry.
 	valueName := ast.NewIdent(name.Name + "_value")
 	valueName.NamePos = newSection
 	valueMap := &ast.StructLit{}
-	d := &ast.Field{Label: valueName, Value: valueMap}
+	d := &ast.Field{Label: valueName, Token: token.ISA, Value: valueMap}
 	// addComments(valueMap, 1, x.Comment, nil)
 
 	if strings.Contains(name.Name, "google") {
@@ -682,6 +682,7 @@
 		// For now we just specify the required fields. This is not correct
 		// but more practical.
 		// Value: &ast.StructLit{}, // Remove to make at least one required.
+		Token: token.ISA,
 	}
 	f.AddComment(comment(x.Comment, true))
 
diff --git a/encoding/protobuf/testdata/attributes.proto.out.cue b/encoding/protobuf/testdata/attributes.proto.out.cue
index 0918e35..dacadf7 100644
--- a/encoding/protobuf/testdata/attributes.proto.out.cue
+++ b/encoding/protobuf/testdata/attributes.proto.out.cue
@@ -20,7 +20,7 @@
 	"time"
 )
 
-StructWrap: {
+StructWrap :: {
 	struct?: {} @protobuf(1,type=google.protobuf.Struct)
 	any?: _ @protobuf(2,type=google.protobuf.Value)
 	listVal?: [...] @protobuf(3,type=google.protobuf.ListValue)
@@ -46,7 +46,7 @@
 // target.service: example
 // ```
 //
-Attributes: {
+Attributes :: {
 	// A map of attribute name to its value.
 	attributes?: {
 		[string]: Attributes_AttributeValue
@@ -54,10 +54,11 @@
 }
 
 // Specifies one attribute value with different type.
-Attributes_AttributeValue: {
+Attributes_AttributeValue :: {
 }
+
 // The attribute value.
-Attributes_AttributeValue: {
+Attributes_AttributeValue :: {
 	// Used for values of type STRING, DNS_NAME, EMAIL_ADDRESS, and URI
 	stringValue: string @protobuf(2,name=string_value)
 } | {
@@ -88,7 +89,7 @@
 }
 
 // Defines a string map.
-Attributes_StringMap: {
+Attributes_StringMap :: {
 	// Holds a set of name/value pairs.
 	entries?: {
 		[string]: string
@@ -102,7 +103,7 @@
 // dictionary instead. The message-level dictionary is carried by the
 // `words` field of this message, the deployment-wide dictionary is determined via
 // configuration.
-CompressedAttributes: {
+CompressedAttributes :: {
 	// The message-level dictionary.
 	words?: [...string] @protobuf(1)
 
@@ -151,7 +152,7 @@
 
 // A map of string to string. The keys and values in this map are dictionary
 // indices (see the [Attributes][istio.mixer.v1.CompressedAttributes] message for an explanation)
-StringMap: {
+StringMap :: {
 	// Holds a set of name/value pairs.
 	entries?: {
 		[string]: int32
diff --git a/encoding/protobuf/testdata/client_config.proto.out.cue b/encoding/protobuf/testdata/client_config.proto.out.cue
index c9088ed..2dca32f 100644
--- a/encoding/protobuf/testdata/client_config.proto.out.cue
+++ b/encoding/protobuf/testdata/client_config.proto.out.cue
@@ -26,7 +26,7 @@
 )
 
 // Specifies the behavior when the client is unable to connect to Mixer.
-NetworkFailPolicy: {
+NetworkFailPolicy :: {
 	// Specifies the behavior when the client is unable to connect to Mixer.
 	policy?: NetworkFailPolicy_FailPolicy @protobuf(1,type=FailPolicy)
 
@@ -42,15 +42,15 @@
 }
 
 // Example of single-value enum.
-NetworkFailPolicy_FailPolicy:
+NetworkFailPolicy_FailPolicy ::
 	// If network connection fails, request is allowed and delivered to the
 	// service.
 	"FAIL_OPEN"
 
-NetworkFailPolicy_FailPolicy_value: FAIL_OPEN: 0
+NetworkFailPolicy_FailPolicy_value :: FAIL_OPEN: 0
 
 // Defines the per-service client configuration.
-ServiceConfig: {
+ServiceConfig :: {
 	// If true, do not call Mixer Check.
 	disableCheckCalls?: bool @protobuf(1,name=disable_check_calls)
 
@@ -91,7 +91,7 @@
 }
 
 // Defines the transport config on how to call Mixer.
-TransportConfig: {
+TransportConfig :: {
 	// The flag to disable check cache.
 	disableCheckCache?: bool @protobuf(1,name=disable_check_cache)
 
@@ -136,7 +136,7 @@
 }
 
 // Defines the client config for HTTP.
-HttpClientConfig: {
+HttpClientConfig :: {
 	// The transport config.
 	transport?: TransportConfig @protobuf(1)
 
@@ -162,7 +162,7 @@
 }
 
 // Defines the client config for TCP.
-TcpClientConfig: {
+TcpClientConfig :: {
 	// The transport config.
 	transport?: TransportConfig @protobuf(1)
 
diff --git a/encoding/protobuf/testdata/gateway.proto.out.cue b/encoding/protobuf/testdata/gateway.proto.out.cue
index 82b141b..30ac2e7 100644
--- a/encoding/protobuf/testdata/gateway.proto.out.cue
+++ b/encoding/protobuf/testdata/gateway.proto.out.cue
@@ -204,7 +204,7 @@
 //
 package v1alpha3
 
-Gateway: {
+Gateway :: {
 	// REQUIRED: A list of server specifications.
 	servers?: [...Server] @protobuf(1)
 
@@ -280,7 +280,7 @@
 //       serverCertificate: /etc/certs/server.pem
 //       privateKey: /etc/certs/privatekey.pem
 // ```
-Server: {
+Server :: {
 	// REQUIRED: The Port on which the proxy should listen for incoming
 	// connections.
 	port?: Port @protobuf(1)
@@ -330,7 +330,7 @@
 	defaultEndpoint?: string @protobuf(5,name=default_endpoint)
 }
 
-Server_TLSOptions: {
+Server_TLSOptions :: {
 	// If set to true, the load balancer will send a 301 redirect for all
 	// http connections, asking the clients to use HTTPS.
 	httpsRedirect?: bool @protobuf(1,name=https_redirect)
@@ -389,7 +389,7 @@
 }
 
 // TLS modes enforced by the proxy
-Server_TLSOptions_TLSmode:
+Server_TLSOptions_TLSmode ::
 	// The SNI string presented by the client will be used as the match
 	// criterion in a VirtualService TLS route to determine the
 	// destination service from the service registry.
@@ -414,7 +414,7 @@
 	// source and the destination are using Istio mTLS to secure traffic.
 	"AUTO_PASSTHROUGH"
 
-Server_TLSOptions_TLSmode_value: {
+Server_TLSOptions_TLSmode_value :: {
 	PASSTHROUGH:      0
 	SIMPLE:           1
 	MUTUAL:           2
@@ -422,13 +422,13 @@
 }
 
 // TLS protocol versions.
-Server_TLSOptions_TLSProtocol: "TLS_AUTO" | // Automatically choose the optimal TLS version.
+Server_TLSOptions_TLSProtocol :: "TLS_AUTO" | // Automatically choose the optimal TLS version.
 	"TLSV1_0" | // TLS version 1.0
 	"TLSV1_1" | // TLS version 1.1
 	"TLSV1_2" | // TLS version 1.2
 	"TLSV1_3" // TLS version 1.3
 
-Server_TLSOptions_TLSProtocol_value: {
+Server_TLSOptions_TLSProtocol_value :: {
 	TLS_AUTO: 0
 	TLSV1_0:  1
 	TLSV1_1:  2
@@ -437,7 +437,7 @@
 }
 
 // Port describes the properties of a specific port of a service.
-Port: {
+Port :: {
 	// REQUIRED: A valid non-negative integer port number.
 	number?: uint32 @protobuf(1)
 
diff --git a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor_proto_gen.cue
index 0c599f5..124c24e 100644
--- a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor_proto_gen.cue
@@ -74,12 +74,12 @@
 
 // The protocol compiler can output a FileDescriptorSet containing the .proto
 // files it parses.
-FileDescriptorSet: {
+FileDescriptorSet :: {
 	file?: [...FileDescriptorProto] @protobuf(1)
 }
 
 // Describes a complete .proto file.
-FileDescriptorProto: {
+FileDescriptorProto :: {
 	name?:    string @protobuf(1) // file name, relative to root of source tree
 	package?: string @protobuf(2) // e.g. "foo", "foo.bar", etc.
 
@@ -112,7 +112,7 @@
 }
 
 // Describes a message type.
-DescriptorProto: {
+DescriptorProto :: {
 	name?: string @protobuf(1)
 	field?: [...FieldDescriptorProto] @protobuf(2)
 	extension?: [...FieldDescriptorProto] @protobuf(6)
@@ -128,7 +128,7 @@
 	reservedName?: [...string] @protobuf(10,name=reserved_name)
 }
 
-DescriptorProto_ExtensionRange: {
+DescriptorProto_ExtensionRange :: {
 	start?:   int32                 @protobuf(1) // Inclusive.
 	end?:     int32                 @protobuf(2) // Exclusive.
 	options?: ExtensionRangeOptions @protobuf(3)
@@ -137,18 +137,18 @@
 // Range of reserved tag numbers. Reserved tag numbers may not be used by
 // fields or extension ranges in the same message. Reserved ranges may
 // not overlap.
-DescriptorProto_ReservedRange: {
+DescriptorProto_ReservedRange :: {
 	start?: int32 @protobuf(1) // Inclusive.
 	end?:   int32 @protobuf(2) // Exclusive.
 }
 
-ExtensionRangeOptions: {
+ExtensionRangeOptions :: {
 	// The parser stores options it doesn't recognize here. See above.
 	uninterpretedOption?: [...UninterpretedOption] @protobuf(999,name=uninterpreted_option)
 }
 
 // Describes a field within a message.
-FieldDescriptorProto: {
+FieldDescriptorProto :: {
 	name?:   string                     @protobuf(1)
 	number?: int32                      @protobuf(3)
 	label?:  FieldDescriptorProto_Label @protobuf(4,type=Label)
@@ -186,7 +186,8 @@
 	jsonName?: string       @protobuf(10,name=json_name)
 	options?:  FieldOptions @protobuf(8)
 }
-FieldDescriptorProto_Type:
+
+FieldDescriptorProto_Type ::
 	"TYPE_DOUBLE" |
 	"TYPE_FLOAT" |
 
@@ -222,7 +223,7 @@
 	// Order is weird for historical reasons.
 	"TYPE_SINT64" // Uses ZigZag encoding.
 
-FieldDescriptorProto_Type_value: {
+FieldDescriptorProto_Type_value :: {
 	"TYPE_DOUBLE":   1
 	"TYPE_FLOAT":    2
 	"TYPE_INT64":    3
@@ -242,26 +243,26 @@
 	"TYPE_SINT32":   17
 	"TYPE_SINT64":   18
 }
-FieldDescriptorProto_Label:
+FieldDescriptorProto_Label ::
 	// 0 is reserved for errors
 	"LABEL_OPTIONAL" |
 	"LABEL_REQUIRED" |
 	"LABEL_REPEATED"
 
-FieldDescriptorProto_Label_value: {
+FieldDescriptorProto_Label_value :: {
 	"LABEL_OPTIONAL": 1
 	"LABEL_REQUIRED": 2
 	"LABEL_REPEATED": 3
 }
 
 // Describes a oneof.
-OneofDescriptorProto: {
+OneofDescriptorProto :: {
 	name?:    string       @protobuf(1)
 	options?: OneofOptions @protobuf(2)
 }
 
 // Describes an enum type.
-EnumDescriptorProto: {
+EnumDescriptorProto :: {
 	name?: string @protobuf(1)
 	value?: [...EnumValueDescriptorProto] @protobuf(2)
 	options?: EnumOptions @protobuf(3)
@@ -282,27 +283,27 @@
 // Note that this is distinct from DescriptorProto.ReservedRange in that it
 // is inclusive such that it can appropriately represent the entire int32
 // domain.
-EnumDescriptorProto_EnumReservedRange: {
+EnumDescriptorProto_EnumReservedRange :: {
 	start?: int32 @protobuf(1) // Inclusive.
 	end?:   int32 @protobuf(2) // Inclusive.
 }
 
 // Describes a value within an enum.
-EnumValueDescriptorProto: {
+EnumValueDescriptorProto :: {
 	name?:    string           @protobuf(1)
 	number?:  int32            @protobuf(2)
 	options?: EnumValueOptions @protobuf(3)
 }
 
 // Describes a service.
-ServiceDescriptorProto: {
+ServiceDescriptorProto :: {
 	name?: string @protobuf(1)
 	method?: [...MethodDescriptorProto] @protobuf(2)
 	options?: ServiceOptions @protobuf(3)
 }
 
 // Describes a method of a service.
-MethodDescriptorProto: {
+MethodDescriptorProto :: {
 	name?: string @protobuf(1)
 
 	// Input and output type names.  These are resolved in the same way as
@@ -318,7 +319,7 @@
 	serverStreaming?: bool @protobuf(6,name=server_streaming,"default=false")
 }
 
-FileOptions: {
+FileOptions :: {
 	// Sets the Java package where classes generated from this .proto will be
 	// placed.  By default, the proto package is used, but this is often
 	// inappropriate because proto packages do not normally start with backwards
@@ -422,19 +423,19 @@
 }
 
 // Generated classes can be optimized for speed or code size.
-FileOptions_OptimizeMode: "SPEED" | // Generate complete code for parsing, serialization,
+FileOptions_OptimizeMode :: "SPEED" | // Generate complete code for parsing, serialization,
 
 	// etc.
 	"CODE_SIZE" |
 	"LITE_RUNTIME" // Generate code using MessageLite and the lite runtime.
 
-FileOptions_OptimizeMode_value: {
+FileOptions_OptimizeMode_value :: {
 	"SPEED":        1
 	"CODE_SIZE":    2 // Use ReflectionOps to implement these methods.
 	"LITE_RUNTIME": 3
 }
 
-MessageOptions: {
+MessageOptions :: {
 	// Set true to use the old proto1 MessageSet wire format for extensions.
 	// This is provided for backwards-compatibility with the MessageSet wire
 	// format.  You should not use this for any other reason:  It's less
@@ -493,7 +494,7 @@
 	uninterpretedOption?: [...UninterpretedOption] @protobuf(999,name=uninterpreted_option)
 }
 
-FieldOptions: {
+FieldOptions :: {
 	// The ctype option instructs the C++ code generator to use a different
 	// representation of the field than it normally would.  See the specific
 	// options below.  This option is not yet implemented in the open source
@@ -562,18 +563,18 @@
 	// The parser stores options it doesn't recognize here. See above.
 	uninterpretedOption?: [...UninterpretedOption] @protobuf(999,name=uninterpreted_option)
 }
-FieldOptions_CType:
+FieldOptions_CType ::
 	// Default mode.
 	"STRING" |
 	"CORD" |
 	"STRING_PIECE"
 
-FieldOptions_CType_value: {
+FieldOptions_CType_value :: {
 	"STRING":       0
 	"CORD":         1
 	"STRING_PIECE": 2
 }
-FieldOptions_JSType:
+FieldOptions_JSType ::
 	// Use the default type.
 	"JS_NORMAL" |
 
@@ -583,18 +584,18 @@
 	// Use JavaScript numbers.
 	"JS_NUMBER"
 
-FieldOptions_JSType_value: {
+FieldOptions_JSType_value :: {
 	"JS_NORMAL": 0
 	"JS_STRING": 1
 	"JS_NUMBER": 2
 }
 
-OneofOptions: {
+OneofOptions :: {
 	// The parser stores options it doesn't recognize here. See above.
 	uninterpretedOption?: [...UninterpretedOption] @protobuf(999,name=uninterpreted_option)
 }
 
-EnumOptions: {
+EnumOptions :: {
 	// Set this option to true to allow mapping different tag names to the same
 	// value.
 	allowAlias?: bool @protobuf(2,name=allow_alias)
@@ -609,7 +610,7 @@
 	uninterpretedOption?: [...UninterpretedOption] @protobuf(999,name=uninterpreted_option)
 }
 
-EnumValueOptions: {
+EnumValueOptions :: {
 	// Is this enum value deprecated?
 	// Depending on the target platform, this can emit Deprecated annotations
 	// for the enum value, or it will be completely ignored; in the very least,
@@ -620,7 +621,7 @@
 	uninterpretedOption?: [...UninterpretedOption] @protobuf(999,name=uninterpreted_option)
 }
 
-ServiceOptions: {
+ServiceOptions :: {
 
 	// Note:  Field numbers 1 through 32 are reserved for Google's internal RPC
 	//   framework.  We apologize for hoarding these numbers to ourselves, but
@@ -637,7 +638,7 @@
 	uninterpretedOption?: [...UninterpretedOption] @protobuf(999,name=uninterpreted_option)
 }
 
-MethodOptions: {
+MethodOptions :: {
 
 	// Note:  Field numbers 1 through 32 are reserved for Google's internal RPC
 	//   framework.  We apologize for hoarding these numbers to ourselves, but
@@ -658,11 +659,11 @@
 // Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
 // or neither? HTTP based RPC implementation may choose GET verb for safe
 // methods, and PUT verb for idempotent methods instead of the default POST.
-MethodOptions_IdempotencyLevel: "IDEMPOTENCY_UNKNOWN" |
+MethodOptions_IdempotencyLevel :: "IDEMPOTENCY_UNKNOWN" |
 	"NO_SIDE_EFFECTS" | // implies idempotent
 	"IDEMPOTENT" // idempotent, but may have side effects
 
-MethodOptions_IdempotencyLevel_value: {
+MethodOptions_IdempotencyLevel_value :: {
 	"IDEMPOTENCY_UNKNOWN": 0
 	"NO_SIDE_EFFECTS":     1
 	"IDEMPOTENT":          2
@@ -674,7 +675,7 @@
 // options protos in descriptor objects (e.g. returned by Descriptor::options(),
 // or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
 // in them.
-UninterpretedOption: {
+UninterpretedOption :: {
 	name?: [...UninterpretedOption_NamePart] @protobuf(2,type=NamePart)
 
 	// The value of the uninterpreted option, in whatever type the tokenizer
@@ -692,14 +693,14 @@
 // extension (denoted with parentheses in options specs in .proto files).
 // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
 // "foo.(bar.baz).qux".
-UninterpretedOption_NamePart: {
+UninterpretedOption_NamePart :: {
 	namePart?:    string @protobuf(1,name=name_part)
 	isExtension?: bool   @protobuf(2,name=is_extension)
 }
 
 // Encapsulates information about the original source file from which a
 // FileDescriptorProto was generated.
-SourceCodeInfo: {
+SourceCodeInfo :: {
 	// A Location identifies a piece of source code in a .proto file which
 	// corresponds to a particular definition.  This information is intended
 	// to be useful to IDEs, code indexers, documentation generators, and similar
@@ -746,7 +747,7 @@
 	location?: [...SourceCodeInfo_Location] @protobuf(1,type=Location)
 }
 
-SourceCodeInfo_Location: {
+SourceCodeInfo_Location :: {
 	// Identifies which part of the FileDescriptorProto was defined at this
 	// location.
 	//
@@ -834,13 +835,13 @@
 // Describes the relationship between generated code and its original source
 // file. A GeneratedCodeInfo message is associated with only one generated
 // source file, but may contain references to different source .proto files.
-GeneratedCodeInfo: {
+GeneratedCodeInfo :: {
 	// An Annotation connects some span of text in generated code to an element
 	// of its generating .proto file.
 	annotation?: [...GeneratedCodeInfo_Annotation] @protobuf(1,type=Annotation)
 }
 
-GeneratedCodeInfo_Annotation: {
+GeneratedCodeInfo_Annotation :: {
 	// Identifies the element in the original source .proto file. This field
 	// is formatted the same as SourceCodeInfo.Location.path.
 	path?: [...int32] @protobuf(1,packed)
diff --git a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/google.golang.org/genproto/googleapis/rpc/status/status_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/google.golang.org/genproto/googleapis/rpc/status/status_proto_gen.cue
index ab7356e..baa5f6d 100644
--- a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/google.golang.org/genproto/googleapis/rpc/status/status_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/google.golang.org/genproto/googleapis/rpc/status/status_proto_gen.cue
@@ -67,7 +67,7 @@
 //
 // - Logging. If some API errors are stored in logs, the message `Status` could
 //     be used directly after any stripping needed for security/privacy reasons.
-Status: {
+Status :: {
 	// The status code, which should be an enum value of
 	// [google.rpc.Code][google.rpc.Code].
 	code?: int32 @protobuf(1)
diff --git a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test/test_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test/test_proto_gen.cue
index efe315a..531a95c 100644
--- a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test/test_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test/test_proto_gen.cue
@@ -1,5 +1,5 @@
 package test_test
 
-AnotherTest: {
+AnotherTest :: {
 	test?: int32 @protobuf(1)
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test_proto_gen.cue
index 329db3e..834c4dd 100644
--- a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test_proto_gen.cue
@@ -1,5 +1,5 @@
 package test
 
-Test: {
+Test :: {
 	test?: int32 @protobuf(1)
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue
index 0918e35..dacadf7 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue
@@ -20,7 +20,7 @@
 	"time"
 )
 
-StructWrap: {
+StructWrap :: {
 	struct?: {} @protobuf(1,type=google.protobuf.Struct)
 	any?: _ @protobuf(2,type=google.protobuf.Value)
 	listVal?: [...] @protobuf(3,type=google.protobuf.ListValue)
@@ -46,7 +46,7 @@
 // target.service: example
 // ```
 //
-Attributes: {
+Attributes :: {
 	// A map of attribute name to its value.
 	attributes?: {
 		[string]: Attributes_AttributeValue
@@ -54,10 +54,11 @@
 }
 
 // Specifies one attribute value with different type.
-Attributes_AttributeValue: {
+Attributes_AttributeValue :: {
 }
+
 // The attribute value.
-Attributes_AttributeValue: {
+Attributes_AttributeValue :: {
 	// Used for values of type STRING, DNS_NAME, EMAIL_ADDRESS, and URI
 	stringValue: string @protobuf(2,name=string_value)
 } | {
@@ -88,7 +89,7 @@
 }
 
 // Defines a string map.
-Attributes_StringMap: {
+Attributes_StringMap :: {
 	// Holds a set of name/value pairs.
 	entries?: {
 		[string]: string
@@ -102,7 +103,7 @@
 // dictionary instead. The message-level dictionary is carried by the
 // `words` field of this message, the deployment-wide dictionary is determined via
 // configuration.
-CompressedAttributes: {
+CompressedAttributes :: {
 	// The message-level dictionary.
 	words?: [...string] @protobuf(1)
 
@@ -151,7 +152,7 @@
 
 // A map of string to string. The keys and values in this map are dictionary
 // indices (see the [Attributes][istio.mixer.v1.CompressedAttributes] message for an explanation)
-StringMap: {
+StringMap :: {
 	// Holds a set of name/value pairs.
 	entries?: {
 		[string]: int32
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/api_spec_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/api_spec_proto_gen.cue
index 6594c92..3e6c50e 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/api_spec_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/api_spec_proto_gen.cue
@@ -71,7 +71,7 @@
 //   api_keys:
 //   - query: api-key
 // ```
-HTTPAPISpec: {
+HTTPAPISpec :: {
 	// List of attributes that are generated when *any* of the HTTP
 	// patterns match. This list typically includes the "api.service"
 	// and "api.version" attributes.
@@ -105,7 +105,7 @@
 //   httpMethod: GET
 //   uriTemplate: /foo/bar
 // ```
-HTTPAPISpecPattern: {
+HTTPAPISpecPattern :: {
 	// List of attributes that are generated if the HTTP request matches
 	// the specified http_method and uri_template. This typically
 	// includes the "api.operation" attribute.
@@ -116,7 +116,7 @@
 	// example: GET, HEAD, POST, PUT, DELETE.
 	httpMethod?: string @protobuf(2,name=http_method)
 }
-HTTPAPISpecPattern: {
+HTTPAPISpecPattern :: {
 	// URI template to match against as defined by
 	// [rfc6570](https://tools.ietf.org/html/rfc6570). For example, the
 	// following are valid URI templates:
@@ -144,9 +144,9 @@
 //
 // See [API Keys](https://swagger.io/docs/specification/authentication/api-keys)
 // for a general overview of API keys as defined by OpenAPI.
-APIKey: {
+APIKey :: {
 }
-APIKey: {
+APIKey :: {
 	// API Key is sent as a query parameter. `query` represents the
 	// query string parameter name.
 	//
@@ -189,7 +189,7 @@
 // - name: foo
 //   namespace: bar
 // ```
-HTTPAPISpecReference: {
+HTTPAPISpecReference :: {
 	// REQUIRED. The short name of the HTTPAPISpec. This is the resource
 	// name defined by the metadata name field.
 	name?: string @protobuf(1)
@@ -217,7 +217,7 @@
 //   - name: petstore
 //     namespace: default
 // ```
-HTTPAPISpecBinding: {
+HTTPAPISpecBinding :: {
 	// REQUIRED. One or more services to map the listed HTTPAPISpec onto.
 	services?: [...IstioService] @protobuf(1)
 
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue
index 0a561d6..a7721c1 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue
@@ -25,7 +25,7 @@
 )
 
 // Specifies the behavior when the client is unable to connect to Mixer.
-NetworkFailPolicy: {
+NetworkFailPolicy :: {
 	// Specifies the behavior when the client is unable to connect to Mixer.
 	policy?: NetworkFailPolicy_FailPolicy @protobuf(1,type=FailPolicy)
 
@@ -41,15 +41,15 @@
 }
 
 // Example of single-value enum.
-NetworkFailPolicy_FailPolicy:
+NetworkFailPolicy_FailPolicy ::
 	// If network connection fails, request is allowed and delivered to the
 	// service.
 	"FAIL_OPEN"
 
-NetworkFailPolicy_FailPolicy_value: "FAIL_OPEN": 0
+NetworkFailPolicy_FailPolicy_value :: "FAIL_OPEN": 0
 
 // Defines the per-service client configuration.
-ServiceConfig: {
+ServiceConfig :: {
 	// If true, do not call Mixer Check.
 	disableCheckCalls?: bool @protobuf(1,name=disable_check_calls)
 
@@ -90,7 +90,7 @@
 }
 
 // Defines the transport config on how to call Mixer.
-TransportConfig: {
+TransportConfig :: {
 	// The flag to disable check cache.
 	disableCheckCache?: bool @protobuf(1,name=disable_check_cache)
 
@@ -135,7 +135,7 @@
 }
 
 // Defines the client config for HTTP.
-HttpClientConfig: {
+HttpClientConfig :: {
 	// The transport config.
 	transport?: TransportConfig @protobuf(1)
 
@@ -161,7 +161,7 @@
 }
 
 // Defines the client config for TCP.
-TcpClientConfig: {
+TcpClientConfig :: {
 	// The transport config.
 	transport?: TransportConfig @protobuf(1)
 
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/quota_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/quota_proto_gen.cue
index c0203a6..6f02c1f 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/quota_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/quota_proto_gen.cue
@@ -54,14 +54,14 @@
 package client
 
 // Determines the quotas used for individual requests.
-QuotaSpec: {
+QuotaSpec :: {
 	// A list of Quota rules.
 	rules?: [...QuotaRule] @protobuf(1)
 }
 
 // Specifies a rule with list of matches and list of quotas.
 // If any clause matched, the list of quotas will be used.
-QuotaRule: {
+QuotaRule :: {
 	// If empty, match all request.
 	// If any of match is true, it is matched.
 	match?: [...AttributeMatch] @protobuf(1)
@@ -72,9 +72,9 @@
 
 // Describes how to match a given string in HTTP headers. Match is
 // case-sensitive.
-StringMatch: {
+StringMatch :: {
 }
-StringMatch: {
+StringMatch :: {
 	// exact string match
 	exact: string @protobuf(1)
 } | {
@@ -86,7 +86,7 @@
 }
 
 // Specifies a match clause to match Istio attributes
-AttributeMatch: {
+AttributeMatch :: {
 	// Map of attribute names to StringMatch type.
 	// Each map element specifies one condition to match.
 	//
@@ -103,7 +103,7 @@
 }
 
 // Specifies a quota to use with quota name and amount.
-Quota: {
+Quota :: {
 	// The quota name to charge
 	quota?: string @protobuf(1)
 
@@ -113,7 +113,7 @@
 
 // QuotaSpecBinding defines the binding between QuotaSpecs and one or more
 // IstioService.
-QuotaSpecBinding: {
+QuotaSpecBinding :: {
 	// REQUIRED. One or more services to map the listed QuotaSpec onto.
 	services?: [...IstioService] @protobuf(1)
 
@@ -125,7 +125,7 @@
 
 // QuotaSpecReference uniquely identifies the QuotaSpec used in the
 // Binding.
-QuotaSpecBinding_QuotaSpecReference: {
+QuotaSpecBinding_QuotaSpecReference :: {
 	// REQUIRED. The short name of the QuotaSpec. This is the resource
 	// name defined by the metadata name field.
 	name?: string @protobuf(1)
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/service_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/service_proto_gen.cue
index d597c18..f218dda 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/service_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/service_proto_gen.cue
@@ -25,7 +25,7 @@
 // IstioService identifies a service and optionally service version.
 // The FQDN of the service is composed from the name, namespace, and implementation-specific domain suffix
 // (e.g. on Kubernetes, "reviews" + "default" + "svc.cluster.local" -> "reviews.default.svc.cluster.local").
-IstioService: {
+IstioService :: {
 	// The short name of the service such as "foo".
 	name?: string @protobuf(1)
 
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue
index d67f638..f710842 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue
@@ -22,7 +22,7 @@
 )
 
 // Used to get a thumbs-up/thumbs-down before performing an action.
-CheckRequest: {
+CheckRequest :: {
 	// The attributes to use for this request.
 	//
 	// Mixer's configuration determines how these attributes are used to
@@ -45,7 +45,7 @@
 }
 
 // parameters for a quota allocation
-CheckRequest_QuotaParams: {
+CheckRequest_QuotaParams :: {
 	// Amount of quota to allocate
 	amount?: int64 @protobuf(1)
 
@@ -54,7 +54,7 @@
 }
 
 // The response generated by the Check method.
-CheckResponse: {
+CheckResponse :: {
 	// The precondition check results.
 	precondition?: CheckResponse_PreconditionResult @protobuf(2,type=PreconditionResult,"(gogoproto.nullable)=false")
 
@@ -65,7 +65,7 @@
 }
 
 // Expresses the result of a precondition check.
-CheckResponse_PreconditionResult: {
+CheckResponse_PreconditionResult :: {
 	// A status code of OK indicates all preconditions were satisfied. Any other code indicates not
 	// all preconditions were satisfied and details describe why.
 	status?: _status_.Status @protobuf(1,type=google.rpc.Status,"(gogoproto.nullable)=false")
@@ -87,7 +87,7 @@
 _status_ = status
 
 // Expresses the result of a quota allocation.
-CheckResponse_QuotaResult: {
+CheckResponse_QuotaResult :: {
 	// The amount of time for which this result can be considered valid.
 	validDuration?: time.Duration @protobuf(1,type=google.protobuf.Duration,name=valid_duration,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
 
@@ -102,7 +102,7 @@
 
 // Describes the attributes that were used to determine the response.
 // This can be used to construct a response cache.
-ReferencedAttributes: {
+ReferencedAttributes :: {
 	// The message-level dictionary. Refer to [CompressedAttributes][istio.mixer.v1.CompressedAttributes] for information
 	// on using dictionaries.
 	words?: [...string] @protobuf(1)
@@ -112,12 +112,12 @@
 }
 
 // How an attribute's value was matched
-ReferencedAttributes_Condition: "CONDITION_UNSPECIFIED" | // should not occur
+ReferencedAttributes_Condition :: "CONDITION_UNSPECIFIED" | // should not occur
 	"ABSENCE" | // match when attribute doesn't exist
 	"EXACT" | // match when attribute value is an exact byte-for-byte match
 	"REGEX" // match when attribute value matches the included regex
 
-ReferencedAttributes_Condition_value: {
+ReferencedAttributes_Condition_value :: {
 	"CONDITION_UNSPECIFIED": 0
 	"ABSENCE":               1
 	"EXACT":                 2
@@ -125,7 +125,7 @@
 }
 
 // Describes a single attribute match.
-ReferencedAttributes_AttributeMatch: {
+ReferencedAttributes_AttributeMatch :: {
 	// The name of the attribute. This is a dictionary index encoded in a manner identical
 	// to all strings in the [CompressedAttributes][istio.mixer.v1.CompressedAttributes] message.
 	name?: int32 @protobuf(1,type=sint32)
@@ -154,7 +154,7 @@
 // names are normalized to lower-case with dashes, e.g.  "x-request-id".
 // Pseudo-headers ":path", ":authority", and ":method" are supported to modify
 // the request headers.
-HeaderOperation: {
+HeaderOperation :: {
 	// Header name.
 	name?: string @protobuf(1)
 
@@ -166,11 +166,11 @@
 }
 
 // Operation type.
-HeaderOperation_Operation: "REPLACE" | // replaces the header with the given name
+HeaderOperation_Operation :: "REPLACE" | // replaces the header with the given name
 	"REMOVE" | // removes the header with the given name (the value is ignored)
 	"APPEND" // appends the value to the header value, or sets it if not present
 
-HeaderOperation_Operation_value: {
+HeaderOperation_Operation_value :: {
 	"REPLACE": 0
 	"REMOVE":  1
 	"APPEND":  2
@@ -178,7 +178,7 @@
 
 // Expresses the routing manipulation actions to be performed on behalf of
 // Mixer in response to a precondition check.
-RouteDirective: {
+RouteDirective :: {
 	// Operations on the request headers.
 	requestHeaderOperations?: [...HeaderOperation] @protobuf(1,name=request_header_operations,"(gogoproto.nullable)=false")
 
@@ -195,7 +195,7 @@
 }
 
 // Used to report telemetry after performing one or more actions.
-ReportRequest: {
+ReportRequest :: {
 
 	// next value: 5
 
@@ -223,7 +223,7 @@
 }
 
 // Used to signal how the sets of compressed attributes should be reconstitued server-side.
-ReportRequest_RepeatedAttributesSemantics:
+ReportRequest_RepeatedAttributesSemantics ::
 	// Use delta encoding between sets of compressed attributes to reduce the overall on-wire
 	// request size. Each individual set of attributes is used to modify the previous set.
 	// NOTE: There is no way with this encoding to specify attribute value deletion. This
@@ -235,11 +235,11 @@
 	// will allow for proper accounting of absent values in overall encoding.
 	"INDEPENDENT_ENCODING"
 
-ReportRequest_RepeatedAttributesSemantics_value: {
+ReportRequest_RepeatedAttributesSemantics_value :: {
 	"DELTA_ENCODING":       0
 	"INDEPENDENT_ENCODING": 1
 }
 
 // Used to carry responses to telemetry reports
-ReportResponse: {
+ReportResponse :: {
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/networking/v1alpha3/gateway_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/networking/v1alpha3/gateway_proto_gen.cue
index 1d301da..c511f8f 100644
--- a/encoding/protobuf/testdata/istio.io/api/networking/v1alpha3/gateway_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/networking/v1alpha3/gateway_proto_gen.cue
@@ -203,7 +203,7 @@
 //
 package v1alpha3
 
-Gateway: {
+Gateway :: {
 	// REQUIRED: A list of server specifications.
 	servers?: [...Server] @protobuf(1)
 
@@ -279,7 +279,7 @@
 //       serverCertificate: /etc/certs/server.pem
 //       privateKey: /etc/certs/privatekey.pem
 // ```
-Server: {
+Server :: {
 	// REQUIRED: The Port on which the proxy should listen for incoming
 	// connections.
 	port?: Port @protobuf(1)
@@ -329,7 +329,7 @@
 	defaultEndpoint?: string @protobuf(5,name=default_endpoint)
 }
 
-Server_TLSOptions: {
+Server_TLSOptions :: {
 	// If set to true, the load balancer will send a 301 redirect for all
 	// http connections, asking the clients to use HTTPS.
 	httpsRedirect?: bool @protobuf(1,name=https_redirect)
@@ -388,7 +388,7 @@
 }
 
 // TLS modes enforced by the proxy
-Server_TLSOptions_TLSmode:
+Server_TLSOptions_TLSmode ::
 	// The SNI string presented by the client will be used as the match
 	// criterion in a VirtualService TLS route to determine the
 	// destination service from the service registry.
@@ -413,7 +413,7 @@
 	// source and the destination are using Istio mTLS to secure traffic.
 	"AUTO_PASSTHROUGH"
 
-Server_TLSOptions_TLSmode_value: {
+Server_TLSOptions_TLSmode_value :: {
 	"PASSTHROUGH":      0
 	"SIMPLE":           1
 	"MUTUAL":           2
@@ -421,13 +421,13 @@
 }
 
 // TLS protocol versions.
-Server_TLSOptions_TLSProtocol: "TLS_AUTO" | // Automatically choose the optimal TLS version.
+Server_TLSOptions_TLSProtocol :: "TLS_AUTO" | // Automatically choose the optimal TLS version.
 	"TLSV1_0" | // TLS version 1.0
 	"TLSV1_1" | // TLS version 1.1
 	"TLSV1_2" | // TLS version 1.2
 	"TLSV1_3" // TLS version 1.3
 
-Server_TLSOptions_TLSProtocol_value: {
+Server_TLSOptions_TLSProtocol_value :: {
 	"TLS_AUTO": 0
 	"TLSV1_0":  1
 	"TLSV1_1":  2
@@ -436,7 +436,7 @@
 }
 
 // Port describes the properties of a specific port of a service.
-Port: {
+Port :: {
 	// REQUIRED: A valid non-negative integer port number.
 	number?: uint32 @protobuf(1)