-
Notifications
You must be signed in to change notification settings - Fork 288
/
value.go
139 lines (119 loc) · 3.34 KB
/
value.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
package value
import (
"fmt"
"github.com/pkg/errors"
"go.starlark.net/starlark"
"github.com/windmilleng/tilt/internal/tiltfile/starkit"
"github.com/windmilleng/tilt/pkg/model"
)
// If `v` is a `starlark.Sequence`, return a slice of its elements
// Otherwise, return it as a single-element slice
// For functions that take `Union[List[T], T]`
func ValueOrSequenceToSlice(v starlark.Value) []starlark.Value {
if seq, ok := v.(starlark.Sequence); ok {
var ret []starlark.Value
it := seq.Iterate()
defer it.Done()
var i starlark.Value
for it.Next(&i) {
ret = append(ret, i)
}
return ret
} else if v == nil || v == starlark.None {
return nil
} else {
return []starlark.Value{v}
}
}
func ValueToStringMap(v starlark.Value) (map[string]string, error) {
var result map[string]string
if v != nil && v != starlark.None {
d, ok := v.(*starlark.Dict)
if !ok {
return nil, fmt.Errorf("expected dict, got %T", v)
}
var err error
result, err = skylarkStringDictToGoMap(d)
if err != nil {
return nil, err
}
}
return result, nil
}
func skylarkStringDictToGoMap(d *starlark.Dict) (map[string]string, error) {
r := map[string]string{}
for _, tuple := range d.Items() {
kV, ok := AsString(tuple[0])
if !ok {
return nil, fmt.Errorf("key is not a string: %T (%v)", tuple[0], tuple[0])
}
k := string(kV)
vV, ok := AsString(tuple[1])
if !ok {
return nil, fmt.Errorf("value is not a string: %T (%v)", tuple[1], tuple[1])
}
v := string(vV)
r[k] = v
}
return r, nil
}
func ValueToAbsPath(thread *starlark.Thread, v starlark.Value) (string, error) {
pathMaker, ok := v.(PathMaker)
if ok {
return pathMaker.MakeLocalPath("."), nil
}
str, ok := v.(starlark.String)
if ok {
return starkit.AbsPath(thread, string(str)), nil
}
return "", fmt.Errorf("expected path | string. Actual type: %T", v)
}
type PathMaker interface {
MakeLocalPath(relPath string) string
}
func SequenceToStringSlice(seq starlark.Sequence) ([]string, error) {
if seq == nil {
return nil, nil
}
it := seq.Iterate()
defer it.Done()
var ret []string
var v starlark.Value
for it.Next(&v) {
s, ok := v.(starlark.String)
if !ok {
return nil, fmt.Errorf("'%v' is a %T, not a string", v, v)
}
ret = append(ret, string(s))
}
return ret, nil
}
func StringSliceToList(slice []string) *starlark.List {
v := []starlark.Value{}
for _, s := range slice {
v = append(v, starlark.String(s))
}
return starlark.NewList(v)
}
// provides dockerfile-style behavior of:
// a string gets interpreted as a shell command (like, sh -c 'foo bar $X')
// an array of strings gets interpreted as a raw argv to exec
func ValueToCmd(v starlark.Value) (model.Cmd, error) {
switch x := v.(type) {
// If a starlark function takes an optional command argument, then UnpackArgs will set its starlark.Value to nil
// we convert nils here to an empty Cmd, since otherwise every callsite would have to do a nil check with presumably
// the same outcome
case nil:
return model.Cmd{}, nil
case starlark.String:
return model.ToShellCmd(string(x)), nil
case starlark.Sequence:
argv, err := SequenceToStringSlice(x)
if err != nil {
return model.Cmd{}, errors.Wrap(err, "a command must be a string or a list of strings")
}
return model.Cmd{Argv: argv}, nil
default:
return model.Cmd{}, fmt.Errorf("a command must be a string or list of strings. found %T", x)
}
}