Skip to content
This repository was archived by the owner on Apr 30, 2025. It is now read-only.

Commit f6ad82e

Browse files
committed
Allow set enum defaults when override a parent value
1 parent c6dbdd0 commit f6ad82e

File tree

3 files changed

+111
-24
lines changed

3 files changed

+111
-24
lines changed

analyze.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ func findRefWithKind(v cue.Value, kinds ...TSType) (ref, referrer cue.Value, has
225225
// appendSplit splits a cue.Value into the
226226
func appendSplit(a []cue.Value, splitBy cue.Op, v cue.Value) []cue.Value {
227227
op, args := v.Expr()
228+
228229
// dedup elements.
229230
k := 1
230231
outer:
@@ -250,6 +251,12 @@ outer:
250251
a = appendSplit(a, splitBy, v)
251252
}
252253
}
254+
255+
if defaultValue, is := v.Default(); is {
256+
if o, _ := v.Expr(); o == cue.NoOp {
257+
a = append(a, defaultValue)
258+
}
259+
}
253260
return a
254261
}
255262

generator.go

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,14 @@ func (g *generator) genInterface(name string, v cue.Value) []ts.Decl {
509509
// Theoretically, lattice equality can be defined as bijective
510510
// subsumption. In practice, Subsume() seems to ignore optional
511511
// fields, and Equals() doesn't. So, use Equals().
512-
if sub.Exists() && sub.Equals(iter.Value()) {
512+
513+
// We need to check if the child overrides the parent. In that case, we have an AndOp that
514+
// tell us that it is setting a value.
515+
op, _ := iter.Value().Expr()
516+
// Also we need to check if the sub operator to discard the one that have validators and if it has a default
517+
subOp, _ := sub.Expr()
518+
_, def := iter.Value().Default()
519+
if sub.Exists() && sub.Equals(iter.Value()) && (subOp == cue.AndOp || op != cue.AndOp || !def) {
513520
continue
514521
}
515522
}
@@ -741,28 +748,28 @@ func (g *generator) genEnumReference(v cue.Value) (*typeRef, error) {
741748
panic("unreachable")
742749
case 1:
743750
case 2:
744-
// The only case we actually want to support, at least for now, is this:
745-
//
746-
// enum: "foo" | "bar" @cuetsy(kind="enum")
747-
// enumref: enum & "foo" @cuetsy(kind="type")
748-
//
749-
// Where we render enumref to TS as `Enumref: Enum.Foo`.
750-
// For that case, we allow at most two conjuncts, and make sure they
751-
// fit the pattern of the two operands above.
752-
aref, bref := isReference(conjuncts[0]), isReference(conjuncts[1])
753-
aconc, bconc := conjuncts[0].IsConcrete(), conjuncts[1].IsConcrete()
754-
var cr cue.Value
755-
if aref {
756-
cr, lit = conjuncts[0], &(conjuncts[1])
757-
} else {
758-
cr, lit = conjuncts[1], &(conjuncts[0])
751+
var err error
752+
lit, err = getEnumLiteral(conjuncts)
753+
if err != nil {
754+
ve := valError(v, err.Error())
755+
g.addErr(ve)
756+
return nil, ve
759757
}
760-
if aref == bref || aconc == bconc || cr.Subsume(*lit) != nil {
761-
ve := valError(v, "may only unify a referenced enum with a concrete literal member of that enum")
758+
case 3:
759+
// It could happen when we are setting a default value into a parent field.
760+
if !conjuncts[0].Equals(conjuncts[1]) {
761+
ve := valError(v, "complex unifications containing references to enums without overriding parent are not currently supported")
762762
g.addErr(ve)
763763
return nil, ve
764764
}
765765

766+
var err error
767+
lit, err = getEnumLiteral(conjuncts[1:])
768+
if err != nil {
769+
ve := valError(v, err.Error())
770+
g.addErr(ve)
771+
return nil, ve
772+
}
766773
default:
767774
ve := valError(v, "complex unifications containing references to enums are not currently supported")
768775
g.addErr(ve)
@@ -807,20 +814,50 @@ func (g *generator) genEnumReference(v cue.Value) (*typeRef, error) {
807814
return nil, err
808815
}
809816
}
810-
case 2:
811-
if typeIdent, err := findIdent(ev, *lit); err == nil {
812-
ref.T = tsast.SelectorExpr{
813-
Expr: ref.T,
814-
Sel: *typeIdent,
815-
}
817+
case 2, 3:
818+
var rr tsast.Expr
819+
if defaultIdent, err := findIdent(ev, *lit); err == nil {
820+
rr = tsast.SelectorExpr{Expr: ref.T, Sel: *defaultIdent}
816821
} else {
817822
return nil, err
818823
}
824+
825+
if _, has := v.Default(); has {
826+
ref.D = rr
827+
} else {
828+
ref.T = rr
829+
}
819830
}
820831

821832
return ref, nil
822833
}
823834

835+
func getEnumLiteral(conjuncts []cue.Value) (*cue.Value, error) {
836+
var lit *cue.Value
837+
// The only case we actually want to support, at least for now, is this:
838+
//
839+
// enum: "foo" | "bar" @cuetsy(kind="enum")
840+
// enumref: enum & "foo" @cuetsy(kind="type")
841+
//
842+
// Where we render enumref to TS as `Enumref: Enum.Foo`.
843+
// For that case, we allow at most two conjuncts, and make sure they
844+
// fit the pattern of the two operands above.
845+
aref, bref := isReference(conjuncts[0]), isReference(conjuncts[1])
846+
aconc, bconc := conjuncts[0].IsConcrete(), conjuncts[1].IsConcrete()
847+
var cr cue.Value
848+
if aref {
849+
cr, lit = conjuncts[0], &(conjuncts[1])
850+
} else {
851+
cr, lit = conjuncts[1], &(conjuncts[0])
852+
}
853+
854+
if aref == bref || aconc == bconc || cr.Subsume(*lit) != nil {
855+
return nil, errors.New(fmt.Sprintf("may only unify a referenced enum with a concrete literal member of that enum. Path: %s", conjuncts[0].Path()))
856+
}
857+
858+
return lit, nil
859+
}
860+
824861
// typeRef is a pair of expressions for referring to another type - the reference
825862
// to the type, and the default value for the referrer. The default value
826863
// may be the one provided by either the referent, or by the field doing the referring
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
-- cue --
2+
3+
#Enum: "a" | "b" | "c" @cuetsy(kind="enum")
4+
5+
#Base: {
6+
valueEnum: #Enum
7+
defaultEnum: #Enum
8+
} @cuetsy(kind="interface")
9+
10+
#StructWithDefaults: {
11+
#Base
12+
baseEnum: #Enum
13+
valueEnum: #Enum & "a"
14+
defaultEnum: #Enum | *"a"
15+
noOverrideDefaultEnum: #Enum | *"b"
16+
noOverrideEnum: #Enum & "b"
17+
} @cuetsy(kind="interface")
18+
19+
-- ts --
20+
21+
export enum Enum {
22+
A = 'a',
23+
B = 'b',
24+
C = 'c',
25+
}
26+
27+
export interface Base {
28+
defaultEnum: Enum;
29+
valueEnum: Enum;
30+
}
31+
32+
export interface StructWithDefaults extends Base {
33+
baseEnum: Enum;
34+
defaultEnum: Enum;
35+
noOverrideDefaultEnum: Enum;
36+
noOverrideEnum: Enum.B;
37+
valueEnum: Enum.A;
38+
}
39+
40+
export const defaultStructWithDefaults: Partial<StructWithDefaults> = {
41+
defaultEnum: Enum.A,
42+
noOverrideDefaultEnum: Enum.B,
43+
};

0 commit comments

Comments
 (0)