-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
fillin.go
116 lines (108 loc) · 2.52 KB
/
fillin.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
package main
import (
"regexp"
"strings"
)
var fillinPattern = regexp.MustCompile(
`{{[A-Za-z][-0-9A-Za-z_]*(:[A-Za-z][-0-9A-Za-z_]*)?}}|\[\[[A-Za-z][-0-9A-Za-z_]*(:[A-Za-z][-0-9A-Za-z_]*)?\]\]`,
)
func collectIdentifiers(args []string) []*Identifier {
var identifiers []*Identifier
for _, arg := range args {
matches := fillinPattern.FindAllString(arg, -1)
for _, match := range matches {
identifiers = append(identifiers, identifierFromMatch(match))
}
}
return identifiers
}
// Fillin fills in the command arguments
func Fillin(args []string, config *Config, p prompt) ([]string, error) {
ret := make([]string, len(args))
if config.Scopes == nil {
config.Scopes = make(map[string]*Scope)
}
values, err := Resolve(collectIdentifiers(args), config, p)
if err != nil {
return nil, err
}
if !empty(values) {
insertValues(config.Scopes, values)
}
for i, arg := range args {
ret[i] = fillinPattern.ReplaceAllStringFunc(arg, func(match string) string {
return lookup(values, identifierFromMatch(match))
})
}
return ret, nil
}
func identifierFromMatch(match string) *Identifier {
match = match[2 : len(match)-2]
var scope, key string
if strings.ContainsRune(match, ':') {
xs := strings.Split(match, ":")
scope = strings.TrimSpace(xs[0])
key = strings.TrimSpace(xs[1])
} else {
key = strings.TrimSpace(match)
}
return &Identifier{scope, key}
}
func insertValues(scopes map[string]*Scope, values map[string]map[string]string) {
for scope := range values {
if _, ok := scopes[scope]; !ok {
scopes[scope] = &Scope{}
}
newValues := make([]map[string]string, 1, len(scopes[scope].Values)+1)
newValues[0] = values[scope]
for _, v := range scopes[scope].Values {
var skip bool
L:
for i, w := range newValues {
switch mapCompare(w, v) {
case mapCompareSubset:
newValues[i] = v
fallthrough
case mapCompareEqual, mapCompareSuperset:
skip = true
break L
}
}
if !skip {
newValues = append(newValues, v)
}
}
scopes[scope].Values = newValues
}
}
const (
mapCompareEqual = iota
mapCompareSuperset
mapCompareSubset
mapCompareDiff
)
func mapCompare(m1, m2 map[string]string) int {
ret := mapCompareEqual
for v, k := range m1 {
if l, ok := m2[v]; ok {
if k != l {
return mapCompareDiff
}
} else {
ret = mapCompareSuperset
}
}
for v, k := range m2 {
if l, ok := m1[v]; ok {
if k != l {
return mapCompareDiff
}
} else {
if ret == mapCompareSuperset {
return mapCompareDiff
}
ret = mapCompareSubset
}
}
return ret
}