Skip to content

Commit 56f120c

Browse files
committed
cue: add Struct type
For most CUE types there is a way to express the type as a Go type in builtin functions. This is not possible for generic structs. This CL adds the Struct type to the cue API. Unfortunately this duplicates some of the API. It could be considered removing some of the old API accordingly. Change-Id: I565fa5215f91786cc5a713713bef9f12d72cbafe Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2684 Reviewed-by: Marcel van Lohuizen <[email protected]>
1 parent 730165c commit 56f120c

File tree

3 files changed

+79
-3
lines changed

3 files changed

+79
-3
lines changed

cue/builtin.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,16 @@ func (c *callCtxt) value(i int) Value {
347347
return newValueRoot(c.ctx, c.args[i])
348348
}
349349

350+
func (c *callCtxt) structVal(i int) *Struct {
351+
v := newValueRoot(c.ctx, c.args[i])
352+
s, err := v.Struct()
353+
if err != nil {
354+
c.invalidArgType(c.args[i], i, "struct", err)
355+
return nil
356+
}
357+
return s
358+
}
359+
350360
func (c *callCtxt) invalidArgType(arg value, i int, typ string, err error) {
351361
if err != nil {
352362
c.errf(c.src, err, "cannot use %s (type %s) as %s in argument %d to %s: %v",

cue/gen.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ func (g *generator) processGo(filename string) {
222222
}
223223
g.defaultPkg = ""
224224
g.name = f.Name.Name
225+
if g.name == "structs" {
226+
g.name = "struct"
227+
}
225228

226229
for _, d := range f.Decls {
227230
switch x := d.(type) {
@@ -396,6 +399,8 @@ func (g *generator) goKind(expr ast.Expr) string {
396399
return "bigRat"
397400
case "internal.Decimal":
398401
return "decimal"
402+
case "cue.Struct":
403+
return "structVal"
399404
case "cue.Value":
400405
return "value"
401406
case "cue.List":
@@ -436,6 +441,8 @@ func (g *generator) goToCUE(expr ast.Expr) (cueKind string, omitCheck bool) {
436441
case "strList":
437442
omitCheck = false
438443
cueKind += "listKind"
444+
case "structVal":
445+
cueKind += "structKind"
439446
case "value":
440447
// Must use callCtxt.value method for these types and resolve manually.
441448
cueKind += "topKind" // TODO: can be more precise

cue/types.go

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,10 @@ func (o *structValue) Lookup(key string) Value {
107107
}
108108
if i == len {
109109
// TODO: better message.
110-
return newValueRoot(o.ctx, o.ctx.mkErr(o.n, codeNotExist,
111-
"value %q not found", key))
110+
ctx := o.ctx
111+
x := ctx.mkErr(o.n, codeNotExist, "value %q not found", key)
112+
v := x.evalPartial(ctx)
113+
return Value{ctx.index, &valueData{o.path, 0, arc{cache: v, v: x}}}
112114
}
113115
// v, _ := o.n.lookup(o.ctx, f)
114116
// v = o.ctx.manifest(v)
@@ -1026,7 +1028,7 @@ func (v Value) structVal(ctx *context) (structValue, *bottom) {
10261028

10271029
// structVal returns an structVal or an error if v is not a struct.
10281030
func (v Value) structValOpts(ctx *context, o options) (structValue, *bottom) {
1029-
v, _ = v.Default()
1031+
v, _ = v.Default() // TODO: remove?
10301032
if err := v.checkKind(ctx, structKind); err != nil {
10311033
return structValue{}, err
10321034
}
@@ -1070,6 +1072,63 @@ func (v Value) structValOpts(ctx *context, o options) (structValue, *bottom) {
10701072
return structValue{ctx, v.path, obj}, nil
10711073
}
10721074

1075+
// Struct returns the underlying struct of a value or an error if the value
1076+
// is not a struct.
1077+
func (v Value) Struct(opts ...Option) (*Struct, error) {
1078+
ctx := v.ctx()
1079+
if err := v.checkKind(ctx, structKind); err != nil {
1080+
return nil, v.toErr(err)
1081+
}
1082+
obj := v.eval(ctx).(*structLit)
1083+
1084+
// TODO: This is expansion appropriate?
1085+
obj = obj.expandFields(ctx)
1086+
1087+
return &Struct{v, obj}, nil
1088+
}
1089+
1090+
// Struct represents a CUE struct value.
1091+
type Struct struct {
1092+
v Value
1093+
s *structLit
1094+
}
1095+
1096+
// FieldInfo contains information about a struct field.
1097+
type FieldInfo struct {
1098+
Name string
1099+
Value Value
1100+
1101+
IsOptional bool
1102+
IsHidden bool
1103+
}
1104+
1105+
// field reports information about the ith field, i < o.Len().
1106+
func (s *Struct) field(i int) FieldInfo {
1107+
ctx := s.v.ctx()
1108+
a := s.s.arcs[i]
1109+
a.cache = s.s.at(ctx, i)
1110+
1111+
v := Value{ctx.index, &valueData{s.v.path, uint32(i), a}}
1112+
str := ctx.labelStr(a.feature)
1113+
return FieldInfo{str, v, a.optional, a.feature&hidden != 0}
1114+
}
1115+
1116+
func (s *Struct) FieldByName(name string) (FieldInfo, bool) {
1117+
f := s.v.ctx().strLabel(name)
1118+
for i, a := range s.s.arcs {
1119+
if a.feature == f {
1120+
return s.field(i), true
1121+
}
1122+
}
1123+
return FieldInfo{}, false
1124+
}
1125+
1126+
// Fields creates an iterator over the Struct's fields.
1127+
func (s *Struct) Fields(opts ...Option) Iterator {
1128+
iter, _ := s.v.Fields(opts...)
1129+
return iter
1130+
}
1131+
10731132
// Fields creates an iterator over v's fields if v is a struct or an error
10741133
// otherwise.
10751134
func (v Value) Fields(opts ...Option) (Iterator, error) {

0 commit comments

Comments
 (0)