package env import ( "errors" "regexp" "sort" "sync" ) type env struct { m map[string]string sync.Mutex } type Env struct { *env p *env } const namePattern = "[a-zA-Z][a-zA-Z0-9.]*" var isNamePattern = regexp.MustCompile("^" + namePattern + "$").MatchString var namePatternRe = regexp.MustCompile("@(" + namePattern + "|{" + namePattern + "})") var errBadVariable = errors.New("bad variable name") var errEmptyVariable = errors.New("empty variable") func New() Env { return Env{env: new(env)} } func (e *env) init() { if e.m == nil { e.m = make(map[string]string) } } func (e *env) Fork() Env { t := New() t.p = e return t } func (e *env) Find(key string) (string, bool) { e.Lock() defer e.Unlock() v, ok := e.m[key] return v, ok } func (e Env) Find(key string) (string, bool) { if e.p != nil { if v, ok := e.p.Find(key); ok { return v, ok } } return e.env.Find(key) } func (e Env) Get(key string) string { v, _ := e.Find(key) return v } func (e *env) Set(key string, value string) error { if !isNamePattern(key) { return errBadVariable } if value == "" { return errEmptyVariable } e.Lock() defer e.Unlock() e.init() e.m[key] = value return nil } func (e *env) Del(key string) bool { e.Lock() defer e.Unlock() if e.m == nil { return false } if _, ok := e.m[key]; !ok { return false } delete(e.m, key) return true } func (e *env) Each(f func(string, string) bool) { var keys []string e.Lock() defer e.Unlock() for k := range e.m { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { if !f(k, e.m[k]) { break } } } func (e *env) Clear() { e.Lock() defer e.Unlock() e.m = nil } func (e Env) Eval(s string) string { repl := func(v string) string { key := v[1:] if key[0] == '{' { key = key[1 : len(key)-1] } return e.Get(key) } for { if t := namePatternRe.ReplaceAllStringFunc(s, repl); t == s { break } else { s = t } } return s }