Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Defaults] reject incomplete cue.Value when generate defaults #34

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 42 additions & 5 deletions generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,9 @@ func (g *generator) genType(name string, v cue.Value) {

tvars["tokens"] = tokens

d, ok := v.Default()
ok, dStr, err := tsPrintDefault(v)
g.addErr(err)
if ok {
dStr, err := tsprintField(d, 0)
g.addErr(err)
tvars["default"] = dStr
}

Expand Down Expand Up @@ -508,13 +507,11 @@ func (g *generator) genInterface(name string, v cue.Value) {
if iter.IsOptional() {
k += "?"
}

vstr, err := tsprintField(iter.Value(), 0)
if err != nil {
g.addErr(err)
return
}

kv := KV{K: k, V: vstr}

exists, defaultV, err := tsPrintDefault(iter.Value())
Expand Down Expand Up @@ -562,6 +559,14 @@ func tsPrintDefault(v cue.Value) (bool, string, error) {
}

if ok {
// when default exists and it is not concrete, we need to reject
isConcret, err := isCueObjConcrete(d)
if err != nil {
return ok, "", valError(d, err.Error())
}
if !isConcret {
return ok, "", valError(d, "cannot generate default values; default value is not concrete")
}
dStr, err := tsprintField(d, 0)
if err != nil {
return false, result, err
Expand All @@ -575,6 +580,38 @@ func tsPrintDefault(v cue.Value) (bool, string, error) {
return false, result, nil
}

func isCueObjConcrete(obj cue.Value) (bool, error) {
// when object is concrete, we still need to check the subelement of struct or list is concrete,
// otherwise we correct the responds on function IsConcrete for the false positive
ok := obj.IsConcrete()
if ok {
var iter cue.Iterator
var err error
if obj.Kind() == cue.ListKind {
iter, err = obj.List()
} else if obj.Kind() == cue.StructKind {
var eleiter *cue.Iterator
eleiter, err = obj.Fields()
iter = *eleiter
}

if err != nil {
return ok, err
}
for iter.Next() {
isSubEleConcret, err := isCueObjConcrete(iter.Value())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: variable naming.

This is more art than science, but a good way i've heard it described is, variable names should get more detailed as the references to them get further away from their declarations. The only references to this are within the same control block, within a few lines of code; it's preferable to use a shorter, even not-particularly-meaningful name.

if err != nil {
return ok, err
}
if !isSubEleConcret {
ok = false
break
}
}
}
return ok, nil
}

// Render a string containing a Typescript semantic equivalent to the provided
// Value for placement in a single field, if possible.
func tsprintField(v cue.Value, nestedLevel int) (string, error) {
Expand Down
5 changes: 5 additions & 0 deletions testdata/defaults_incomplete1_error.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- cue --
A: int | *string @cuetsy(kind="type")

-- err --
cannot generate default values; default value is not concrete
5 changes: 5 additions & 0 deletions testdata/defaults_incomplete2_error.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- cue --
B: "foo" | *("bar" | "baz") @cuetsy(kind="type")

-- err --
cannot generate default values; default value is not concrete
12 changes: 12 additions & 0 deletions testdata/defaults_incomplete3_error.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- cue --
ATypedList: [...int] | *[1,2] @cuetsy(kind="type")

Foo: {
Bar: string | *"ohai"
Baz: int | *1
I1_TypedList: *[...int] | [int,2]
I2_TypedList: ATypedList
} @cuetsy(kind="interface")

-- ts --
cannot generate default values; default value is not concrete