167 lines
3.9 KiB
Go
167 lines
3.9 KiB
Go
// 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"
|
|
)
|
|
|
|
const (
|
|
// SepChar separator char split var name and default value
|
|
SepChar = "|"
|
|
VarLeft = "${" // default var left format chars
|
|
VarRight = "}" // default var right format chars
|
|
|
|
mustPrefix = '?' // must prefix char
|
|
)
|
|
|
|
// 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
|
|
// var format chars for expression.
|
|
// default left="${", right="}"
|
|
VarLeft, VarRight string
|
|
}
|
|
|
|
func (opt *ParseOpts) useDefaultRegex() {
|
|
opt.Regexp = envRegex
|
|
opt.VarLeft = VarLeft
|
|
opt.VarRight = VarRight
|
|
}
|
|
|
|
// 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.
|
|
//
|
|
// see Parser.Parse
|
|
func Parse(val string) (string, error) {
|
|
return std.Parse(val)
|
|
}
|
|
|
|
// SafeParse parse ENV var value from input string, support default value.
|
|
//
|
|
// see Parser.Parse
|
|
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}
|
|
opts.useDefaultRegex()
|
|
|
|
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.
|
|
// ${VAR_NAME1}/path/${VAR_NAME2} Allow multi var name.
|
|
func (p *Parser) Parse(val string) (newVal string, err error) {
|
|
if p.Regexp == nil {
|
|
p.useDefaultRegex()
|
|
}
|
|
|
|
times := strings.Count(val, p.VarLeft)
|
|
if times == 0 {
|
|
return val, nil
|
|
}
|
|
|
|
// enhance: see https://github.com/gookit/goutil/issues/135
|
|
if times == 1 && strings.HasPrefix(val, p.VarLeft) && strings.HasSuffix(val, p.VarRight) {
|
|
return p.parseOne(val)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// 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.
|
|
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] == mustPrefix {
|
|
msg := "value is required for var: " + name
|
|
if len(def) > 1 {
|
|
msg = def[1:]
|
|
}
|
|
err = errors.New(msg)
|
|
} else {
|
|
val = def
|
|
}
|
|
}
|
|
return
|
|
}
|