@@ -292,11 +292,11 @@ func (g *generator) genDef(path cue.Path, val cue.Value) (*generatedDef, error)
292
292
def := & generatedDef {inProgress : true }
293
293
g .def = def
294
294
g .generatedTypes [qpath ] = def
295
- info , err := g .emitType (val , optionalZero )
295
+ facts , err := g .emitType (val , optionalZero )
296
296
if err != nil {
297
297
return nil , err
298
298
}
299
- g .def .facts = info
299
+ g .def .facts = facts
300
300
g .def .inProgress = false
301
301
g .def = parentDef
302
302
return def , nil
@@ -356,10 +356,10 @@ func (g *generator) emitType(val cue.Value, optionalStg optionalStrategy) (typeF
356
356
// Note that type references don't get optionalStg,
357
357
// as @go(,optional=) only affects fields under the current type expression.
358
358
// TODO: support nullable types, such as `null | #SomeReference` and `null | {foo: int}`.
359
- if done , info , err := g .emitTypeReference (val ); err != nil {
360
- return info , err
359
+ if done , facts , err := g .emitTypeReference (val ); err != nil {
360
+ return typeFacts {} , err
361
361
} else if done {
362
- return info , nil
362
+ return facts , nil
363
363
}
364
364
365
365
// Inline types are below.
@@ -443,18 +443,18 @@ func (g *generator) emitType(val cue.Value, optionalStg optionalStrategy) (typeF
443
443
}
444
444
g .def .printf ("%s " , goName )
445
445
446
- // Pointers in Go are a prefix in the syntax, but we won't find out the generated type info
446
+ // Pointers in Go are a prefix in the syntax, but we won't find out the generated type facts
447
447
// until we have emitted its Go source, which we do into the same buffer to avoid copies.
448
448
// Luckily, since a pointer is always one byte, and we gofmt the result anyway for nice formatting,
449
449
// we can add the pointer first and replace it with whitespace later if not wanted.
450
450
ptrOffset := len (g .def .src )
451
451
g .def .printf ("*" )
452
452
453
- info , err := g .emitType (val , optionalStg )
453
+ facts , err := g .emitType (val , optionalStg )
454
454
if err != nil {
455
- return info , err
455
+ return facts , err
456
456
}
457
- if ! usePointer (info , optional , optionalStg ) {
457
+ if ! usePointer (facts , optional , optionalStg ) {
458
458
g .def .src [ptrOffset ] = ' '
459
459
}
460
460
@@ -522,8 +522,8 @@ func (g *generator) emitType(val cue.Value, optionalStg optionalStrategy) (typeF
522
522
return facts , nil
523
523
}
524
524
525
- func usePointer (info typeFacts , optional bool , strategy optionalStrategy ) bool {
526
- if info .isTypeOverride {
525
+ func usePointer (facts typeFacts , optional bool , strategy optionalStrategy ) bool {
526
+ if facts .isTypeOverride {
527
527
// @(,type=) overrides any @(,optional=) setting
528
528
return false
529
529
}
@@ -536,7 +536,7 @@ func usePointer(info typeFacts, optional bool, strategy optionalStrategy) bool {
536
536
return false
537
537
case optionalNillable :
538
538
// Only use a pointer when the type isn't already nillable.
539
- return ! info .isNillable
539
+ return ! facts .isNillable
540
540
default :
541
541
panic ("unreachable" )
542
542
}
@@ -690,26 +690,19 @@ func goPkgNameForInstance(inst *build.Instance, instVal cue.Value) string {
690
690
// emitTypeReference attempts to generate a CUE value as a Go type via a reference,
691
691
// either to a type in the same Go package, or to a type in an imported package.
692
692
func (g * generator ) emitTypeReference (val cue.Value ) (bool , typeFacts , error ) {
693
- var facts typeFacts
694
693
// References to existing names, either from the same package or an imported package.
695
694
root , path := val .ReferencePath ()
696
695
// TODO: surely there is a better way to check whether ReferencePath returned "no path",
697
696
// such as a possible path.IsValid method?
698
697
if len (path .Selectors ()) == 0 {
699
- return false , facts , nil
698
+ return false , typeFacts {} , nil
700
699
}
701
700
inst := root .BuildInstance ()
702
701
// Go has no notion of qualified import paths; if a CUE file imports
703
702
// "foo.com/bar:qualified", we import just "foo.com/bar" on the Go side.
704
703
// TODO: deal with multiple packages existing in the same directory.
705
704
unqualifiedPath := ast .ParseImportPath (inst .ImportPath ).Unqualified ().String ()
706
705
707
- var sb strings.Builder
708
- if root != g .pkgRoot {
709
- sb .WriteString (goPkgNameForInstance (inst , root ))
710
- sb .WriteString ("." )
711
- }
712
-
713
706
// As a special case, some CUE standard library types are allowed as references
714
707
// even though they aren't definitions.
715
708
defsOnly := true
@@ -718,33 +711,50 @@ func (g *generator) emitTypeReference(val cue.Value) (bool, typeFacts, error) {
718
711
// Note that CUE represents durations as strings, but Go as int64.
719
712
// TODO: can we do better here, such as a custom duration type?
720
713
g .def .printf ("string /* CUE time.Duration */" )
721
- return true , facts , nil
714
+ return true , typeFacts {} , nil
722
715
case "time.Time" :
723
716
defsOnly = false
724
717
}
725
718
726
719
name := goNameFromPath (path , defsOnly )
727
720
if name == "" {
728
- return false , facts , nil // Not a path we are generating.
721
+ return false , typeFacts {} , nil // Not a path we are generating.
729
722
}
730
723
731
- sb .WriteString (name )
732
- g .def .printf ("%s" , sb .String ())
733
-
724
+ var facts typeFacts
725
+ inProgress := false
734
726
// We did use a reference; if the referenced name was from another package,
735
727
// we need to ensure that package is imported.
736
728
// Otherwise, we need to ensure that the referenced local definition is generated.
737
729
// Either way, return the facts about the referenced type.
738
730
if root != g .pkgRoot {
739
- // TODO: populate the facts here, which will require generating imported packages first.
740
731
g .importCuePkgAsGoPkg [inst .ImportPath ] = unqualifiedPath
741
- return true , facts , nil
732
+ // TODO: populate the facts here, which will require generating imported packages first.
733
+ } else {
734
+ def , err := g .genDef (path , cue .Dereference (val ))
735
+ if err != nil {
736
+ return false , typeFacts {}, err
737
+ }
738
+ facts = def .facts
739
+ inProgress = def .inProgress
742
740
}
743
- def , err := g .genDef (path , cue .Dereference (val ))
744
- if err != nil {
745
- return false , facts , err
741
+ // We generate types depth-first; if the type referenced here is still in progress,
742
+ // it means that we are in a cyclic type, so be nillable to avoid a Go type of infinite size.
743
+ // Note that sometimes we're in a complex type which is already nillable, such as:
744
+ //
745
+ // #GraphNode: {edges?: [...#GraphNode]}
746
+ //
747
+ // So we could generate the Go field as `[]GraphNode` rather than `[]*GraphNode`,
748
+ // given that Go slices are already nillable, but we currently do use a pointer.
749
+ if inProgress && ! facts .isNillable {
750
+ g .def .printf ("*" )
751
+ facts .isNillable = true // pointers can be nil
752
+ }
753
+ if root != g .pkgRoot {
754
+ g .def .printf ("%s." , goPkgNameForInstance (inst , root ))
746
755
}
747
- return true , def .facts , nil
756
+ g .def .printf ("%s" , name )
757
+ return true , facts , nil
748
758
}
749
759
750
760
// emitDocs generates the documentation comments attached to the following declaration.
0 commit comments