package env import ( "errors" "regexp" "sort" "sync" ) type env struct { m map[string]string sync.Mutex } type Env struct { *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") func New() Env { return Env{new(env)} } func (e *env) init() { if e.m == nil { e.m = make(map[string]string) } } func (e *env) Copy() Env { c := New() if len(e.m) > 0 { c.init() for k, v := range e.m { c.m[k] = v } } return c } func (e *env) Find(key string) (string, bool) { e.Lock() defer e.Unlock() v, ok := e.m[key] return v, ok } 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 } 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 { e.Lock() defer e.Unlock() repl := func(v string) string { key := v[1:] if key[0] == '{' { key = key[1 : len(key)-1] } if v, ok := e.m[key]; ok { return v } return "" } for { if t := namePatternRe.ReplaceAllStringFunc(s, repl); t == s { break } else { s = t } } return s }