Skip to content

Commit

Permalink
thema: Additional assignability checks
Browse files Browse the repository at this point in the history
  • Loading branch information
sam boyer committed Jul 12, 2023
1 parent 80c1181 commit 375c1b4
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 12 deletions.
27 changes: 15 additions & 12 deletions assignable.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,29 +88,28 @@ func assignable(sch cue.Value, T interface{}) error {

check = func(gval, sval cue.Value, p cue.Path) {
sk, gk := sval.IncompleteKind(), gval.IncompleteKind()
schemaHadNull, goHadNull := sk&cue.NullKind != 0, gk&cue.NullKind != 0
schemaHadNull := sk&cue.NullKind != 0
sk &^= cue.NullKind
gk &^= cue.NullKind

ogval := gval
if goHadNull {
// At least for now, we have to deal with these unhelpful *null
// appearing in the encoding of pointer types.
//
// We can't do the same for the schema side, because this null stripper
// relies on the fact that all actual Go type declarations will come across
// as a single value, without any disjunctions.
gval = stripLeadNull(gval)
}
// At least for now, we have to deal with these unhelpful *null
// appearing in the encoding of pointer types.
//
// We can't do the same for the schema side, because this null stripper
// relies on the fact that all actual Go type declarations will come across
// as a single value, without any disjunctions.
gval = stripLeadNull(gval)

// strict equality _might_ be too restrictive? But it's better to start there
if sk != gk && gk != cue.TopKind {
if sk != gk && gk != (cue.TopKind^cue.NullKind) {
errs[p.String()] = fmt.Errorf("%s: is kind %s in schema, but kind %s in Go type", p, sk, gk)
return
} else if gk == cue.TopKind {
} else if gk == (cue.TopKind ^ cue.NullKind) {
// Escape hatch for a Go interface{}/any
return
}
op, _ := sval.Expr()

switch sk {
case cue.ListKind:
Expand All @@ -122,6 +121,10 @@ func assignable(sch cue.Value, T interface{}) error {
checkscalar(gval, sval, p)
}
case cue.StructKind:
if op == cue.OrOp {
errs[p.String()] = fmt.Errorf("%s: contains disjunction over struct types, but Go type is not any", p)
return
}
checkstruct(gval, sval, p)
case cue.NullKind:
errs[p.String()] = fmt.Errorf("%s: null is not permitted in schema; express optionality with ?", p)
Expand Down
27 changes: 27 additions & 0 deletions assignable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,33 @@ func TestAssignable(t *testing.T) {
}
`,
},
"any": {
T: &struct {
AString any `json:"aString"`
AnInt any `json:"anInt"`
}{},
cue: `typ: {
aString: string
anInt: int32
}
`,
},
"not-any-union": {
T: &struct {
Hopeful struct {
AString any `json:"aString"`
} `json:"hopeful"`
}{},
cue: `typ: {
hopeful: {
aString: string
} | {
anInt: int32
}
}
`,
invalid: true,
},
"stringEnumNoPointer": {
T: struct {
Foo string `json:"foo"`
Expand Down

0 comments on commit 375c1b4

Please sign in to comment.