Skip to content

Commit 9ef3b70

Browse files
committed
internal/encoding/gotypes: implement optional=nillable
This requires recording "facts" about how we generated each Go type, such as whether it came from a `@go(type=)` override or whether the resulting Go type is nillable or not. This now works for inline types, as well as references to generated local types. References to generated types from imported packages is left as a TODO for a follow-up, given the size of this patch. Document the feature as well, and clean up the help text a bit. For example, it didn't use the same "experimental" prefix we already used in other commands like `cue mod mirror`. Fixes #3760. Signed-off-by: Daniel Martí <[email protected]> Change-Id: Ib2dd8ace6eb75462ca9247ca06613ab3ba5bcafd Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1214380 TryBot-Result: CUEcueckoo <[email protected]> Unity-Result: CUE porcuepine <[email protected]> Reviewed-by: Roger Peppe <[email protected]>
1 parent defc384 commit 9ef3b70

File tree

3 files changed

+151
-74
lines changed

3 files changed

+151
-74
lines changed

cmd/cue/cmd/exp.go

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,52 +40,57 @@ as the objective is to gain experience and then move the feature elsewhere.
4040
return cmd
4141
}
4242

43-
// TODO(mvdan): document the "optional" attribute option when finished.
44-
4543
func newExpGenGoTypesCmd(c *Command) *cobra.Command {
4644
cmd := &cobra.Command{
4745
Use: "gengotypes",
4846
Short: "generate Go types from CUE definitions",
49-
Long: `
50-
gengotypes generates Go type definitions from exported CUE definitions.
47+
Long: `WARNING: THIS COMMAND IS EXPERIMENTAL.
5148
52-
*This command is experimental and may be changed at any time - see "cue help exp"*
49+
gengotypes generates Go type definitions from exported CUE definitions.
5350
5451
The generated Go types are guaranteed to accept any value accepted by the CUE definitions,
5552
but may be more general. For example, "string | int" will translate into the Go
56-
type "any" because the Go type system is not able to express
57-
disjunctions.
53+
type "any" because the Go type system is not able to express disjunctions.
5854
5955
To ensure that the resulting Go code works, any imported CUE packages or
6056
referenced CUE definitions are transitively generated as well.
61-
The generated code is placed in cue_types*_gen.go files in the directory of
62-
each CUE package.
57+
Generated code is placed in cue_types*_gen.go files in each CUE package directory.
6358
6459
Generated Go type and field names may differ from the original CUE names by default.
6560
For instance, an exported definition "#foo" becomes "Foo",
66-
given that Go uses capitalization to export names in a package,
67-
and a nested definition like "#foo.#bar" becomes "Foo_Bar",
68-
given that Go does not allow declaring nested types.
61+
and a nested definition like "#foo.#bar" becomes "Foo_Bar".
6962
70-
@go attributes can be used to override which name or type to be generated, for example:
63+
@go attributes can be used to override which name to be generated:
7164
7265
package foo
7366
@go(betterpkgname)
7467
7568
#Bar: {
7669
@go(BetterBarTypeName)
7770
renamed: int @go(BetterFieldName)
78-
79-
retypedLocal: [...string] @go(,type=[]LocalType)
80-
retypedImport: [...string] @go(,type=[]"foo.com/bar".ImportedType)
8171
}
8272
83-
The attribute "@go(-)" can be used to ignore a definition or field, for example:
73+
The attribute "@go(-)" can be used to ignore a definition or field:
8474
8575
#ignoredDefinition: {
8676
@go(-)
8777
}
8878
ignoredField: int @go(-)
79+
80+
"type=" overrides an entire value to generate as a given Go type expression:
81+
82+
retypedLocal: [string]: int @go(,type=map[LocalType]int)
83+
retypedImport: [...string] @go(,type=[]"foo.com/bar".ImportedType)
84+
85+
"optional=" controls how CUE optional fields are generated as Go fields.
86+
The default is "zero", representing a missing field as the zero value.
87+
"nillable" ensures the generated Go type can represent missing fields as nil.
88+
89+
optionalDefault?: int // generates as "int64"
90+
optionalNillable?: int @go(,optional=nillable) // generates as "*int64"
91+
nested: {
92+
@go(,optional=nillable) // set for all fields under this struct
93+
}
8994
`[1:],
9095
// TODO: write a long help text once the feature set is reasonably stable.
9196
RunE: mkRunE(c, runExpGenGoTypes),

cmd/cue/cmd/testdata/script/exp_gengotypes.txtar

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -289,19 +289,20 @@ func main() {
289289
zeroRoot.Fields.OptionalList = make([]int64, 0)
290290
zeroRoot.Fields.OptionalMap = make(map[string]int64, 0)
291291

292-
zeroRoot.Fields.OptionalTopAttrNillable = (*any)(nil)
293-
zeroRoot.Fields.OptionalNullAttrNillable = (**struct{})(nil)
292+
zeroRoot.Fields.OptionalTopAttrNillable = (any)(nil)
293+
zeroRoot.Fields.OptionalNullAttrNillable = (*struct{})(nil)
294294
zeroRoot.Fields.OptionalBasicAttrNillable = (*int64)(nil)
295-
zeroRoot.Fields.OptionalListAttrNillable = (*[]int64)(nil)
295+
zeroRoot.Fields.OptionalListAttrNillable = ([]int64)(nil)
296296

297-
zeroRoot.Fields.OptionalInlineMapAttrNillable = (*map[string]int64)(nil)
298-
zeroRoot.Fields.OptionalLocalMapAttrNillable = (*root.LocalMap)(nil)
299-
zeroRoot.Fields.OptionalInlineNestedAttrNillable = (*struct{F *[]string `json:"f,omitempty"`})(nil)
297+
zeroRoot.Fields.OptionalInlineMapAttrNillable = (map[string]int64)(nil)
298+
zeroRoot.Fields.OptionalLocalMapAttrNillable = (root.LocalMap)(nil)
299+
zeroRoot.Fields.OptionalInlineNestedAttrNillable = (*struct{F []string `json:"f,omitempty"`})(nil)
300300
zeroRoot.Fields.OptionalLocalNestedAttrNillable = (*root.LocalNested)(nil)
301301

302302
// #localNested is not affected by one of the references using optional=nullable.
303303
var _ = root.LocalNested{F: []string{}}
304304

305+
// TODO: we should notice when types from imported packages are nillable already.
305306
zeroRootImports.OptionalRemoteMapAttrNillable = (*imported.RemoteMap)(nil)
306307
zeroRootImports.OptionalRemoteNestedAttrNillable = (*imported.RemoteNested)(nil)
307308

@@ -739,20 +740,20 @@ type Root struct {
739740

740741
OptionalMap map[string]int64 `json:"optionalMap,omitempty"`
741742

742-
OptionalTopAttrNillable *any/* CUE top */ `json:"optionalTopAttrNillable,omitempty"`
743+
OptionalTopAttrNillable any/* CUE top */ `json:"optionalTopAttrNillable,omitempty"`
743744

744-
OptionalNullAttrNillable **struct{}/* CUE null */ `json:"optionalNullAttrNillable,omitempty"`
745+
OptionalNullAttrNillable *struct{}/* CUE null */ `json:"optionalNullAttrNillable,omitempty"`
745746

746747
OptionalBasicAttrNillable *int64 `json:"optionalBasicAttrNillable,omitempty"`
747748

748-
OptionalListAttrNillable *[]int64 `json:"optionalListAttrNillable,omitempty"`
749+
OptionalListAttrNillable []int64 `json:"optionalListAttrNillable,omitempty"`
749750

750-
OptionalInlineMapAttrNillable *map[string]int64 `json:"optionalInlineMapAttrNillable,omitempty"`
751+
OptionalInlineMapAttrNillable map[string]int64 `json:"optionalInlineMapAttrNillable,omitempty"`
751752

752-
OptionalLocalMapAttrNillable *LocalMap `json:"optionalLocalMapAttrNillable,omitempty"`
753+
OptionalLocalMapAttrNillable LocalMap `json:"optionalLocalMapAttrNillable,omitempty"`
753754

754755
OptionalInlineNestedAttrNillable *struct {
755-
F *[]string `json:"f,omitempty"`
756+
F []string `json:"f,omitempty"`
756757
} `json:"optionalInlineNestedAttrNillable,omitempty"`
757758

758759
OptionalLocalNestedAttrNillable *LocalNested `json:"optionalLocalNestedAttrNillable,omitempty"`

0 commit comments

Comments
 (0)