elvish/pkg/eval/vars/env_list.go

90 lines
2.0 KiB
Go
Raw Normal View History

package vars
import (
"errors"
"os"
"strings"
"sync"
"github.com/elves/elvish/pkg/diag"
2019-12-24 04:00:59 +08:00
"github.com/elves/elvish/pkg/eval/vals"
"github.com/xiaq/persistent/vector"
)
var (
pathListSeparator = string(os.PathListSeparator)
forbiddenInPath = pathListSeparator + "\x00"
)
// Errors
var (
ErrPathMustBeString = errors.New("path must be string")
ErrPathCannotContainColonZero = errors.New(`path cannot contain colon or \0`)
)
2020-09-05 05:24:08 +08:00
// NewEnvListVar returns a variable whose value is a list synchronized with an
// environment variable with the elements joined by os.PathListSeparator.
//
// Elements in the value of the variable must be strings, and cannot contain
// os.PathListSeparator or \0; attempting to put any in its elements will result in
// an error.
func NewEnvListVar(name string) Var {
2020-09-05 05:24:08 +08:00
return &envListVar{envName: name}
}
type envListVar struct {
sync.RWMutex
envName string
cacheFor string
cacheValue interface{}
}
// Get returns a Value for an EnvPathList.
2020-09-05 05:24:08 +08:00
func (envli *envListVar) Get() interface{} {
envli.Lock()
defer envli.Unlock()
value := os.Getenv(envli.envName)
if value == envli.cacheFor {
return envli.cacheValue
}
envli.cacheFor = value
v := vector.Empty
for _, path := range strings.Split(value, pathListSeparator) {
v = v.Cons(path)
}
envli.cacheValue = v
return envli.cacheValue
}
// Set sets an EnvPathList. The underlying environment variable is set.
2020-09-05 05:24:08 +08:00
func (envli *envListVar) Set(v interface{}) error {
var (
paths []string
errElement error
)
2018-02-15 17:14:05 +08:00
errIterate := vals.Iterate(v, func(v interface{}) bool {
s, ok := v.(string)
if !ok {
errElement = ErrPathMustBeString
return false
}
path := s
if strings.ContainsAny(path, forbiddenInPath) {
errElement = ErrPathCannotContainColonZero
return false
}
paths = append(paths, s)
return true
})
if errElement != nil || errIterate != nil {
return diag.Errors(errElement, errIterate)
}
envli.Lock()
defer envli.Unlock()
os.Setenv(envli.envName, strings.Join(paths, pathListSeparator))
return nil
}