github.com/elves/elvish@v0.15.0/pkg/eval/vars/env_list.go (about)

     1  package vars
     2  
     3  import (
     4  	"errors"
     5  	"os"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/elves/elvish/pkg/diag"
    10  	"github.com/elves/elvish/pkg/eval/vals"
    11  	"github.com/xiaq/persistent/vector"
    12  )
    13  
    14  var (
    15  	pathListSeparator = string(os.PathListSeparator)
    16  	forbiddenInPath   = pathListSeparator + "\x00"
    17  )
    18  
    19  // Errors
    20  var (
    21  	ErrPathMustBeString           = errors.New("path must be string")
    22  	ErrPathCannotContainColonZero = errors.New(`path cannot contain colon or \0`)
    23  )
    24  
    25  // NewEnvListVar returns a variable whose value is a list synchronized with an
    26  // environment variable with the elements joined by os.PathListSeparator.
    27  //
    28  // Elements in the value of the variable must be strings, and cannot contain
    29  // os.PathListSeparator or \0; attempting to put any in its elements will result in
    30  // an error.
    31  func NewEnvListVar(name string) Var {
    32  	return &envListVar{envName: name}
    33  }
    34  
    35  type envListVar struct {
    36  	sync.RWMutex
    37  	envName    string
    38  	cacheFor   string
    39  	cacheValue interface{}
    40  }
    41  
    42  // Get returns a Value for an EnvPathList.
    43  func (envli *envListVar) Get() interface{} {
    44  	envli.Lock()
    45  	defer envli.Unlock()
    46  
    47  	value := os.Getenv(envli.envName)
    48  	if value == envli.cacheFor {
    49  		return envli.cacheValue
    50  	}
    51  	envli.cacheFor = value
    52  	v := vector.Empty
    53  	for _, path := range strings.Split(value, pathListSeparator) {
    54  		v = v.Cons(path)
    55  	}
    56  	envli.cacheValue = v
    57  	return envli.cacheValue
    58  }
    59  
    60  // Set sets an EnvPathList. The underlying environment variable is set.
    61  func (envli *envListVar) Set(v interface{}) error {
    62  	var (
    63  		paths      []string
    64  		errElement error
    65  	)
    66  	errIterate := vals.Iterate(v, func(v interface{}) bool {
    67  		s, ok := v.(string)
    68  		if !ok {
    69  			errElement = ErrPathMustBeString
    70  			return false
    71  		}
    72  		path := s
    73  		if strings.ContainsAny(path, forbiddenInPath) {
    74  			errElement = ErrPathCannotContainColonZero
    75  			return false
    76  		}
    77  		paths = append(paths, s)
    78  		return true
    79  	})
    80  
    81  	if errElement != nil || errIterate != nil {
    82  		return diag.Errors(errElement, errIterate)
    83  	}
    84  
    85  	envli.Lock()
    86  	defer envli.Unlock()
    87  	os.Setenv(envli.envName, strings.Join(paths, pathListSeparator))
    88  	return nil
    89  }