-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
pick.go
150 lines (122 loc) · 3.57 KB
/
pick.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package structural
import (
"cuelang.org/go/cue"
"cuelang.org/go/cue/errors"
// "cuelang.org/go/cue/errors"
"github.com/hofstadter-io/cuetils/cmd/cuetils/flags"
)
// PickGlobs will pick a subobject from globs on disk
func PickGlobs(code string, globs []string, opts *flags.RootPflagpole) ([]GlobResult, error) {
return BinaryOpGlobs(code, globs, opts, PickValue)
}
// PickValue uses 'pick' to pick a subvalue from 'from'
// by checking if values unify
func PickValue(pick, from cue.Value, opts *flags.RootPflagpole) (cue.Value, error) {
p, _ := pickValue(pick, from, opts)
return p, nil
}
// this is the recursive version that also returns
// whether the value was picked
func pickValue(pick, from cue.Value, opts *flags.RootPflagpole) (cue.Value, bool) {
// TODO, implement proper default helper func
// for flags in hofmod-cli
if opts == nil {
opts = &flags.RootPflagpole{}
}
switch pick.IncompleteKind() {
// pick everything
case cue.TopKind:
return from, true
// recurse on matching labels
case cue.StructKind:
return pickStruct(pick, from, opts)
case cue.ListKind:
return pickList(pick, from, opts)
default:
return pickLeaf(pick, from, opts)
}
}
func pickStruct(pick, from cue.Value, opts *flags.RootPflagpole) (cue.Value, bool) {
ctx := pick.Context()
if k := from.IncompleteKind(); k != cue.StructKind {
if opts.NodeTypeErrors {
e := errors.Newf(pick.Pos(), "mask type '%v' does not match target value type '%v'", pick.IncompleteKind(), from.IncompleteKind())
ev := ctx.MakeError(e)
return ev, true
}
}
result := newStruct(ctx)
iter, _ := pick.Fields(defaultWalkOptions...)
cnt := 0
for iter.Next() {
cnt++
s := iter.Selector()
p := cue.MakePath(s)
f := from.LookupPath(p)
// fmt.Println(cnt, iter.Value(), f, f.Exists())
// check that field exists in from. Should we be checking f.Err()?
if f.Exists() {
r, ok := pickValue(iter.Value(), f, opts)
// fmt.Println("r:", r, ok, p)
if ok {
result = result.FillPath(p, r)
}
}
}
// need to check for {...}
// no fields and open
if cnt == 0 && pick.Allows(cue.AnyString) {
return from, true
}
// fmt.Println("result:", result)
return result, true
return pick, false
}
func pickList(pick, from cue.Value, opts *flags.RootPflagpole) (cue.Value, bool) {
ctx := pick.Context()
if k := from.IncompleteKind(); k != cue.ListKind {
// should this return or just continue? do we need some way to specify?
// probably prefer to be more strict, so that you know your schemas
// return errors.Newf(from.Pos(), "expected list, but got %v", k), true
return newStruct(ctx), false
}
lpt, err := getListProcType(pick)
if err != nil {
ce := errors.Newf(pick.Pos(), "%v", err)
ev := ctx.MakeError(ce)
return ev, true
}
_ = lpt
// how to consider different list sizes
// if len(pick) == 1, apply to all elements
// if len(pick) > 1
// attributes? @pick(and,or,pos)
// maybe we don't care about length if attribute is used?
pi, _ := pick.List()
fi, _ := from.List()
result := []cue.Value{}
for pi.Next() && fi.Next() {
p, ok := pickValue(pi.Value(), fi.Value(), opts)
if ok {
result = append(result, p)
}
}
return ctx.NewList(result...), true
}
func pickLeaf(pick, from cue.Value, opts *flags.RootPflagpole) (cue.Value, bool) {
// if pick is concrete, so must from
// make sure 1 does not pick int
// but we do want int to pick any num
if pick.IsConcrete() {
if from.IsConcrete() {
r := pick.Unify(from)
return r, r.Exists()
} else {
return cue.Value{}, false
}
} else {
r := pick.Unify(from)
return r, r.Exists()
}
return pick, false
}