@@ -214,15 +214,15 @@ func (g *generator) genType(name string, v cue.Value) []ts.Decl {
214
214
switch op {
215
215
case cue .OrOp :
216
216
for _ , dv := range dvals {
217
- tok , err := tsprintField (dv , true )
217
+ tok , err := g . tsprintField (dv , true )
218
218
if err != nil {
219
219
g .addErr (err )
220
220
return nil
221
221
}
222
222
tokens = append (tokens , tok )
223
223
}
224
224
case cue .NoOp , cue .RegexMatchOp :
225
- tok , err := tsprintField (v , true )
225
+ tok , err := g . tsprintField (v , true )
226
226
if err != nil {
227
227
g .addErr (err )
228
228
return nil
@@ -246,7 +246,7 @@ func (g *generator) genType(name string, v cue.Value) []ts.Decl {
246
246
return ret [:1 ]
247
247
}
248
248
249
- val , err := tsprintField (d , false )
249
+ val , err := g . tsprintField (d , false )
250
250
g .addErr (err )
251
251
252
252
def := tsast.VarDecl {
@@ -657,7 +657,7 @@ func (g *generator) genInterfaceField(v cue.Value) (*typeRef, error) {
657
657
658
658
tref := & typeRef {}
659
659
var err error
660
- tref .T , err = tsprintField (v , true )
660
+ tref .T , err = g . tsprintField (v , true )
661
661
if err != nil {
662
662
if ! containsCuetsyReference (v ) {
663
663
g .addErr (valError (v , "could not generate field: %w" , err ))
@@ -667,7 +667,7 @@ func (g *generator) genInterfaceField(v cue.Value) (*typeRef, error) {
667
667
return nil , nil
668
668
}
669
669
670
- exists , defExpr , err := tsPrintDefault (v )
670
+ exists , defExpr , err := g . tsPrintDefault (v )
671
671
if exists {
672
672
tref .D = defExpr
673
673
}
@@ -729,30 +729,15 @@ func hasTypeReference(v cue.Value) bool {
729
729
func (g * generator ) genEnumReference (v cue.Value ) (* typeRef , error ) {
730
730
var lit * cue.Value
731
731
732
- findIdent := func (ev , tv cue.Value ) (* tsast.Ident , error ) {
733
- if ev .Subsume (tv ) != nil {
734
- err := valError (v , "may only apply values to an enum that are members of that enum; %#v is not a member of %#v" , tv , ev )
735
- g .addErr (err )
736
- return nil , err
737
- }
738
- pairs , err := enumPairs (ev )
739
- if err != nil {
740
- return nil , err
741
- }
742
- for _ , pair := range pairs {
743
- if veq (pair .val , tv ) {
744
- return & tsast.Ident {Name : pair .name }, nil
745
- }
746
- }
747
-
748
- panic (fmt .Sprintf ("unreachable - %#v not equal to any member of %#v, but should have been caught by subsume check" , tv , ev ))
749
- }
750
-
751
732
conjuncts := appendSplit (nil , cue .AndOp , v )
733
+ var enumUnions map [cue.Value ]cue.Value
752
734
switch len (conjuncts ) {
753
735
case 0 :
754
736
panic ("unreachable" )
755
737
case 1 :
738
+ // This case is when we have a union of enums which we need to iterate them to get their values or has a default value.
739
+ // It retrieves a list of literals with their references.
740
+ enumUnions = g .findEnumUnions (v )
756
741
case 2 :
757
742
var err error
758
743
conjuncts [1 ] = getDefaultEnumValue (conjuncts [1 ])
@@ -789,15 +774,15 @@ func (g *generator) genEnumReference(v cue.Value) (*typeRef, error) {
789
774
// Search the expr tree for the actual enum. This approach is uncomfortable
790
775
// without having the assurance that there aren't more than one possible match/a
791
776
// guarantee from the CUE API of a stable, deterministic search order, etc.
792
- ev , referrer , has := findRefWithKind (v , TypeEnum )
777
+ enumValues , referrer , has := findRefWithKind (v , TypeEnum )
793
778
if ! has {
794
779
ve := valError (v , "does not reference a field with a cuetsy enum attribute" )
795
780
g .addErr (ve )
796
781
return nil , fmt .Errorf ("no enum attr in %s" , v )
797
782
}
798
783
799
784
var err error
800
- decls := g .genEnum ("foo" , ev )
785
+ decls := g .genEnum ("foo" , enumValues )
801
786
ref := & typeRef {}
802
787
803
788
// Construct the type component of the reference
@@ -807,7 +792,7 @@ func (g *generator) genEnumReference(v cue.Value) (*typeRef, error) {
807
792
g .addErr (ve )
808
793
return nil , ve
809
794
case 1 , 2 :
810
- ref .T , err = referenceValueAs (referrer )
795
+ ref .T , err = referenceValueAs (referrer , TypeEnum )
811
796
if err != nil {
812
797
panic (err )
813
798
}
@@ -818,19 +803,34 @@ func (g *generator) genEnumReference(v cue.Value) (*typeRef, error) {
818
803
switch len (conjuncts ) {
819
804
case 1 :
820
805
if defv , hasdef := v .Default (); hasdef {
821
- if defaultIdent , err := findIdent (ev , defv ); err == nil {
822
- ref .D = tsast.SelectorExpr {Expr : ref .T , Sel : * defaultIdent }
823
- } else {
824
- return nil , err
825
- }
806
+ err = g .findIdent (v , enumValues , defv , func (expr tsast.Ident ) {
807
+ ref .D = tsast.SelectorExpr {Expr : ref .T , Sel : expr }
808
+ })
809
+ }
810
+ if len (enumUnions ) == 0 {
811
+ break
826
812
}
813
+ var elements []tsast.Expr
814
+ for lit , enumValues := range enumUnions {
815
+ err = g .findIdent (v , enumValues , lit , func (ident tsast.Ident ) {
816
+ elements = append (elements , tsast.SelectorExpr {
817
+ Expr : ref .T ,
818
+ Sel : ident ,
819
+ })
820
+ })
821
+ }
822
+
823
+ // To avoid to change the order of the elements everytime that we generate the code.
824
+ sort .Slice (elements , func (i , j int ) bool {
825
+ return elements [i ].String () < elements [j ].String ()
826
+ })
827
+
828
+ ref .T = ts .Union (elements ... )
827
829
case 2 , 3 :
828
830
var rr tsast.Expr
829
- if defaultIdent , err := findIdent (ev , * lit ); err == nil {
830
- rr = tsast.SelectorExpr {Expr : ref .T , Sel : * defaultIdent }
831
- } else {
832
- return nil , err
833
- }
831
+ err = g .findIdent (v , enumValues , * lit , func (ident tsast.Ident ) {
832
+ rr = tsast.SelectorExpr {Expr : ref .T , Sel : ident }
833
+ })
834
834
835
835
op , args := v .Expr ()
836
836
hasInnerDefault := false
@@ -845,7 +845,64 @@ func (g *generator) genEnumReference(v cue.Value) (*typeRef, error) {
845
845
}
846
846
}
847
847
848
- return ref , nil
848
+ return ref , err
849
+ }
850
+
851
+ // findEnumUnions find the unions between enums like (#Enum & "a") | (#Enum & "b")
852
+ func (g generator ) findEnumUnions (v cue.Value ) map [cue.Value ]cue.Value {
853
+ op , values := v .Expr ()
854
+ if op != cue .OrOp {
855
+ return nil
856
+ }
857
+
858
+ enumsWithUnions := make (map [cue.Value ]cue.Value , len (values ))
859
+ for _ , val := range values {
860
+ conjuncts := appendSplit (nil , cue .AndOp , val )
861
+ if len (conjuncts ) != 2 {
862
+ return nil
863
+ }
864
+ cr , lit := conjuncts [0 ], conjuncts [1 ]
865
+ if cr .Subsume (lit ) != nil {
866
+ return nil
867
+ }
868
+
869
+ switch val .Kind () {
870
+ case cue .StringKind , cue .IntKind :
871
+ enumValues , _ , has := findRefWithKind (v , TypeEnum )
872
+ if ! has {
873
+ return nil
874
+ }
875
+ enumsWithUnions [lit ] = enumValues
876
+ default :
877
+ _ , vals := val .Expr ()
878
+ if len (vals ) > 1 {
879
+ panic (fmt .Sprintf ("%s.%s isn't a valid enum value" , val .Path ().String (), vals [1 ]))
880
+ }
881
+ panic (fmt .Sprintf ("Invalid value in path %s" , val .Path ().String ()))
882
+ }
883
+ }
884
+
885
+ return enumsWithUnions
886
+ }
887
+
888
+ func (g generator ) findIdent (v , ev , tv cue.Value , fn func (tsast.Ident )) error {
889
+ if ev .Subsume (tv ) != nil {
890
+ err := valError (v , "may only apply values to an enum that are members of that enum; %#v is not a member of %#v" , tv , ev )
891
+ g .addErr (err )
892
+ return err
893
+ }
894
+ pairs , err := enumPairs (ev )
895
+ if err != nil {
896
+ return err
897
+ }
898
+ for _ , pair := range pairs {
899
+ if veq (pair .val , tv ) {
900
+ fn (tsast.Ident {Name : pair .name })
901
+ return nil
902
+ }
903
+ }
904
+
905
+ panic (fmt .Sprintf ("unreachable - %#v not equal to any member of %#v, but should have been caught by subsume check" , tv , ev ))
849
906
}
850
907
851
908
func getEnumLiteral (conjuncts []cue.Value ) (* cue.Value , error ) {
@@ -904,7 +961,7 @@ type typeRef struct {
904
961
D ts.Expr
905
962
}
906
963
907
- func tsPrintDefault (v cue.Value ) (bool , ts.Expr , error ) {
964
+ func ( g generator ) tsPrintDefault (v cue.Value ) (bool , ts.Expr , error ) {
908
965
d , ok := v .Default ()
909
966
// [...number] results in [], which is a fake default, we need to correct it here.
910
967
// if ok && d.Kind() == cue.ListKind {
@@ -933,7 +990,7 @@ func tsPrintDefault(v cue.Value) (bool, ts.Expr, error) {
933
990
// }
934
991
935
992
if ok {
936
- expr , err := tsprintField (d , false )
993
+ expr , err := g . tsprintField (d , false )
937
994
if err != nil {
938
995
return false , nil , err
939
996
}
@@ -959,12 +1016,17 @@ func tsPrintDefault(v cue.Value) (bool, ts.Expr, error) {
959
1016
960
1017
// Render a string containing a Typescript semantic equivalent to the provided
961
1018
// Value for placement in a single field, if possible.
962
- func tsprintField (v cue.Value , isType bool ) (ts.Expr , error ) {
1019
+ func ( g generator ) tsprintField (v cue.Value , isType bool ) (ts.Expr , error ) {
963
1020
// Let the forceText attribute supersede everything.
964
1021
if ft := getForceText (v ); ft != "" {
965
1022
return ts .Raw (ft ), nil
966
1023
}
967
1024
1025
+ if hasEnumReference (v ) {
1026
+ ref , err := g .genEnumReference (v )
1027
+ return ref .T , err
1028
+ }
1029
+
968
1030
// References are orthogonal to the Kind system. Handle them first.
969
1031
if hasTypeReference (v ) || containsCuetsyReference (v , TypeInterface ) || hasEnumReference (v ) {
970
1032
ref , err := referenceValueAs (v )
@@ -996,7 +1058,7 @@ func tsprintField(v cue.Value, isType bool) (ts.Expr, error) {
996
1058
// It skips structs like {...} (cue.TopKind) to avoid undesired results.
997
1059
val := v .LookupPath (cue .MakePath (cue .AnyString ))
998
1060
if val .Exists () && val .IncompleteKind () != cue .TopKind {
999
- expr , err := tsprintField (val , isType )
1061
+ expr , err := g . tsprintField (val , isType )
1000
1062
if err != nil {
1001
1063
return nil , valError (v , err .Error ())
1002
1064
}
@@ -1017,7 +1079,7 @@ func tsprintField(v cue.Value, isType bool) (ts.Expr, error) {
1017
1079
size , _ := v .Len ().Int64 ()
1018
1080
kvs := make ([]tsast.KeyValueExpr , 0 , size )
1019
1081
for iter .Next () {
1020
- expr , err := tsprintField (iter .Value (), isType )
1082
+ expr , err := g . tsprintField (iter .Value (), isType )
1021
1083
if err != nil {
1022
1084
return nil , valError (v , err .Error ())
1023
1085
}
@@ -1047,7 +1109,7 @@ func tsprintField(v cue.Value, isType bool) (ts.Expr, error) {
1047
1109
iter , _ := v .List ()
1048
1110
var elems []ts.Expr
1049
1111
for iter .Next () {
1050
- e , err := tsprintField (iter .Value (), isType )
1112
+ e , err := g . tsprintField (iter .Value (), isType )
1051
1113
if err != nil {
1052
1114
return nil , err
1053
1115
}
@@ -1059,12 +1121,11 @@ func tsprintField(v cue.Value, isType bool) (ts.Expr, error) {
1059
1121
case cue .BytesKind :
1060
1122
return nil , valError (v , "bytes have no equivalent in Typescript; use double-quotes (string) instead" )
1061
1123
}
1062
-
1063
1124
// Handler for disjunctions
1064
1125
disj := func (dvals []cue.Value ) (ts.Expr , error ) {
1065
1126
parts := make ([]ts.Expr , 0 , len (dvals ))
1066
1127
for _ , dv := range dvals {
1067
- p , err := tsprintField (dv , isType )
1128
+ p , err := g . tsprintField (dv , isType )
1068
1129
if err != nil {
1069
1130
return nil , err
1070
1131
}
@@ -1113,7 +1174,7 @@ func tsprintField(v cue.Value, isType bool) (ts.Expr, error) {
1113
1174
1114
1175
e := v .LookupPath (cue .MakePath (cue .AnyIndex ))
1115
1176
if e .Exists () {
1116
- expr , err := tsprintField (e , isType )
1177
+ expr , err := g . tsprintField (e , isType )
1117
1178
if err != nil {
1118
1179
return nil , err
1119
1180
}
@@ -1152,7 +1213,7 @@ func tsprintField(v cue.Value, isType bool) (ts.Expr, error) {
1152
1213
switch op {
1153
1214
case cue .OrOp :
1154
1215
if len (dvals ) == 2 && dvals [0 ].Kind () == cue .NullKind {
1155
- return tsprintField (dvals [1 ], isType )
1216
+ return g . tsprintField (dvals [1 ], isType )
1156
1217
}
1157
1218
return disj (dvals )
1158
1219
case cue .NoOp , cue .AndOp :
0 commit comments