-
Notifications
You must be signed in to change notification settings - Fork 181
/
varexpr.go
145 lines (127 loc) · 3.34 KB
/
varexpr.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
// Package varexpr provides some commonly ENV var parse functions.
//
// parse env value, allow expressions:
//
// ${VAR_NAME} Only var name
// ${VAR_NAME | default} With default value, if value is empty.
// ${VAR_NAME | ?error} With error on value is empty.
//
// Examples:
//
// only key - "${SHELL}"
// with default - "${NotExist | defValue}"
// multi key - "${GOPATH}/${APP_ENV | prod}/dir"
package varexpr
import (
"errors"
"os"
"regexp"
"strings"
)
// SepChar separator char
const SepChar = "|"
// ParseOptFn option func
type ParseOptFn func(o *ParseOpts)
// ParseOpts parse options for ParseValue
type ParseOpts struct {
// Getter Env value provider func.
Getter func(string) string
// ParseFn custom parse expr func. expr like "${SHELL}" "${NotExist|defValue}"
ParseFn func(string) (string, error)
// Regexp custom expression regex.
Regexp *regexp.Regexp
// Keyword check chars for expression. default is "${"
Keyword string
}
// must add "?" - To ensure that there is no greedy match
var envRegex = regexp.MustCompile(`\${.+?}`)
var std = New()
// Parse parse ENV var value from input string, support default value.
//
// Format:
//
// ${var_name} Only var name
// ${var_name | default} With default value
// ${var_name | ?error} With error on value is empty.
func Parse(val string) (string, error) {
return std.Parse(val)
}
// SafeParse parse ENV var value from input string, support default value.
func SafeParse(val string) string {
s, _ := std.Parse(val)
return s
}
// ParseWith parse ENV var value from input string, support default value.
func ParseWith(val string, optFns ...ParseOptFn) (string, error) {
return New(optFns...).Parse(val)
}
// Parser parse ENV var value from input string, support default value.
type Parser struct {
ParseOpts
}
// New create a new Parser
func New(optFns ...ParseOptFn) *Parser {
opts := &ParseOpts{
Getter: os.Getenv,
Regexp: envRegex,
Keyword: "${",
}
for _, fn := range optFns {
fn(opts)
}
return &Parser{ParseOpts: *opts}
}
// Parse parse ENV var value from input string, support default value.
//
// Format:
//
// ${var_name} Only var name
// ${var_name | default} With default value
// ${var_name | ?error} With error on value is empty.
func (p *Parser) Parse(val string) (newVal string, err error) {
if p.Regexp == nil {
p.Regexp = envRegex
}
if p.Keyword != "" && !strings.Contains(val, p.Keyword) {
return val, nil
}
// parse expression
newVal = p.Regexp.ReplaceAllStringFunc(val, func(s string) string {
if err != nil {
return s
}
s, err = p.parseOne(s)
return s
})
return
}
// parse one node expression.
func (p *Parser) parseOne(eVar string) (val string, err error) {
if p.ParseFn != nil {
return p.ParseFn(eVar)
}
// eVar like "${NotExist|defValue}", first remove "${" and "}", then split it
ss := strings.SplitN(eVar[2:len(eVar)-1], SepChar, 2)
var name, def string
// with default value. ${NotExist|defValue}
if len(ss) == 2 {
name, def = strings.TrimSpace(ss[0]), strings.TrimSpace(ss[1])
} else {
name = strings.TrimSpace(ss[0])
}
// get ENV value by name
val = p.Getter(name)
if val == "" && def != "" {
// check def is "?error"
if def[0] == '?' {
msg := "value is required for var: " + name
if len(def) > 1 {
msg = def[1:]
}
err = errors.New(msg)
} else {
val = def
}
}
return
}