2016-02-19 09:12:10 +08:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"os"
|
|
|
|
"strings"
|
2016-02-19 21:26:01 +08:00
|
|
|
"sync"
|
2016-02-19 09:12:10 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// Errors
|
|
|
|
var (
|
2016-02-19 19:07:38 +08:00
|
|
|
ErrCanOnlyAssignList = errors.New("can only assign compatible values")
|
2016-02-19 09:12:10 +08:00
|
|
|
ErrPathMustBeString = errors.New("path must be string")
|
|
|
|
ErrPathCannotContainColonZero = errors.New(`path cannot contain colon or \0`)
|
|
|
|
)
|
|
|
|
|
|
|
|
// EnvPathList is a variable whose value is constructed from an environment
|
|
|
|
// variable by splitting at colons. Changes to it are also propagated to the
|
|
|
|
// corresponding environment variable. Its elements cannot contain colons or
|
|
|
|
// \0; attempting to put colon or \0 in its elements will result in an error.
|
|
|
|
//
|
2016-02-19 19:19:19 +08:00
|
|
|
// EnvPathList implements both Value and Variable interfaces. It also satisfied
|
|
|
|
// ListLike.
|
2016-02-19 09:12:10 +08:00
|
|
|
type EnvPathList struct {
|
2016-02-19 21:26:01 +08:00
|
|
|
sync.RWMutex
|
2016-02-19 09:12:10 +08:00
|
|
|
envName string
|
|
|
|
cachedValue string
|
|
|
|
cachedPaths []string
|
|
|
|
}
|
|
|
|
|
2016-02-19 19:19:19 +08:00
|
|
|
var (
|
|
|
|
_ Variable = (*EnvPathList)(nil)
|
|
|
|
_ Value = (*EnvPathList)(nil)
|
|
|
|
_ ListLike = (*EnvPathList)(nil)
|
|
|
|
)
|
|
|
|
|
2016-02-19 09:12:10 +08:00
|
|
|
func (epl *EnvPathList) Get() Value {
|
|
|
|
return epl
|
|
|
|
}
|
|
|
|
|
|
|
|
func (epl *EnvPathList) Set(v Value) {
|
2016-02-19 19:07:38 +08:00
|
|
|
elemser, ok := v.(Elemser)
|
2016-02-19 09:12:10 +08:00
|
|
|
if !ok {
|
|
|
|
throw(ErrCanOnlyAssignList)
|
|
|
|
}
|
2016-02-19 19:07:38 +08:00
|
|
|
var paths []string
|
|
|
|
for v := range elemser.Elems() {
|
2016-02-19 09:12:10 +08:00
|
|
|
s, ok := v.(String)
|
|
|
|
if !ok {
|
|
|
|
throw(ErrPathMustBeString)
|
|
|
|
}
|
|
|
|
path := string(s)
|
|
|
|
if strings.ContainsAny(path, ":\x00") {
|
|
|
|
throw(ErrPathCannotContainColonZero)
|
|
|
|
}
|
2016-02-19 19:07:38 +08:00
|
|
|
paths = append(paths, string(s))
|
2016-02-19 09:12:10 +08:00
|
|
|
}
|
|
|
|
epl.set(paths)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (epl *EnvPathList) Kind() string {
|
|
|
|
return "list"
|
|
|
|
}
|
|
|
|
|
2016-02-20 03:11:31 +08:00
|
|
|
func (epl *EnvPathList) Repr(indent int) string {
|
2016-02-19 09:12:10 +08:00
|
|
|
var b ListReprBuilder
|
2016-02-20 03:11:31 +08:00
|
|
|
b.Indent = indent
|
2016-02-19 21:26:01 +08:00
|
|
|
for _, path := range epl.get() {
|
2016-02-19 09:12:10 +08:00
|
|
|
b.WriteElem(quote(path))
|
|
|
|
}
|
|
|
|
return b.String()
|
|
|
|
}
|
|
|
|
|
2016-02-19 19:19:19 +08:00
|
|
|
func (epl *EnvPathList) Len() int {
|
2016-02-19 21:26:01 +08:00
|
|
|
return len(epl.get())
|
2016-02-19 19:19:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (epl *EnvPathList) Elems() <-chan Value {
|
|
|
|
ch := make(chan Value)
|
|
|
|
go func() {
|
2016-02-19 21:26:01 +08:00
|
|
|
for _, p := range epl.get() {
|
2016-02-19 19:19:19 +08:00
|
|
|
ch <- String(p)
|
|
|
|
}
|
|
|
|
close(ch)
|
|
|
|
}()
|
|
|
|
return ch
|
|
|
|
}
|
|
|
|
|
2016-02-19 09:12:10 +08:00
|
|
|
func (epl *EnvPathList) IndexOne(idx Value) Value {
|
2016-02-19 21:26:01 +08:00
|
|
|
paths := epl.get()
|
|
|
|
i := intIndexWithin(idx, len(paths))
|
|
|
|
return String(paths[i])
|
2016-02-19 09:12:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (epl *EnvPathList) IndexSet(idx, v Value) {
|
|
|
|
s, ok := v.(String)
|
|
|
|
if !ok {
|
|
|
|
throw(ErrPathMustBeString)
|
|
|
|
}
|
2016-02-19 21:26:01 +08:00
|
|
|
|
|
|
|
paths := epl.get()
|
|
|
|
i := intIndexWithin(idx, len(paths))
|
|
|
|
|
|
|
|
epl.Lock()
|
|
|
|
defer epl.Unlock()
|
|
|
|
paths[i] = string(s)
|
|
|
|
epl.syncFromPaths()
|
2016-02-19 09:12:10 +08:00
|
|
|
}
|
|
|
|
|
2016-02-19 21:26:01 +08:00
|
|
|
func (epl *EnvPathList) get() []string {
|
2016-02-21 05:25:26 +08:00
|
|
|
epl.Lock()
|
|
|
|
defer epl.Unlock()
|
2016-02-19 21:26:01 +08:00
|
|
|
|
2016-02-19 09:12:10 +08:00
|
|
|
value := os.Getenv(epl.envName)
|
|
|
|
if value == epl.cachedValue {
|
2016-02-19 21:26:01 +08:00
|
|
|
return epl.cachedPaths
|
2016-02-19 09:12:10 +08:00
|
|
|
}
|
|
|
|
epl.cachedValue = value
|
|
|
|
epl.cachedPaths = strings.Split(value, ":")
|
|
|
|
return epl.cachedPaths
|
|
|
|
}
|
|
|
|
|
|
|
|
func (epl *EnvPathList) set(paths []string) {
|
2016-02-19 21:26:01 +08:00
|
|
|
epl.Lock()
|
|
|
|
defer epl.Unlock()
|
|
|
|
|
2016-02-19 09:12:10 +08:00
|
|
|
epl.cachedPaths = paths
|
2016-02-19 21:26:01 +08:00
|
|
|
epl.syncFromPaths()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (epl *EnvPathList) syncFromPaths() {
|
|
|
|
epl.cachedValue = strings.Join(epl.cachedPaths, ":")
|
2016-02-19 09:12:10 +08:00
|
|
|
err := os.Setenv(epl.envName, epl.cachedValue)
|
|
|
|
maybeThrow(err)
|
|
|
|
}
|