go.mway.dev/x@v0.0.0-20240520034138-950aede9a3fb/env/path.go (about) 1 // Copyright (c) 2023 Matt Way 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to 5 // deal in the Software without restriction, including without limitation the 6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 // sell copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 // IN THE THE SOFTWARE. 20 21 package env 22 23 import ( 24 "bytes" 25 "fmt" 26 "os" 27 ) 28 29 const _envKeyPath = "PATH" 30 31 // Path represents $PATH within the environment. 32 type Path struct { 33 v *Var 34 } 35 36 // NewPath returns a [Path] that holds the current value of $PATH. 37 func NewPath() Path { 38 return Path{ 39 v: NewVar(_envKeyPath), 40 } 41 } 42 43 // Prepend prepends paths to the current value of p. 44 func (p Path) Prepend(paths ...string) (newpath Path, err error) { 45 defer func() { 46 if err != nil { 47 err = fmt.Errorf("failed to prepend path(s): %w", err) 48 } 49 }() 50 51 if len(paths) == 0 { 52 return p, nil 53 } 54 55 // Calculate how much extra storage is needed. 56 extra := len(paths) // for delimiters 57 for _, path := range paths { 58 extra += len(path) 59 } 60 61 // Prepend all of the path fragments to the current value. 62 var buf bytes.Buffer 63 buf.Grow(len(p.v.Value()) + extra) 64 for _, path := range paths { 65 if len(path) == 0 { 66 continue 67 } 68 69 buf.WriteString(path) //nolint:errcheck 70 buf.WriteByte(os.PathListSeparator) //nolint:errcheck 71 } 72 73 // Write the current value after any prepended path(s). 74 buf.WriteString(p.Value()) //nolint:errcheck 75 76 // Create a copy of this Path and persist the change to the copy (and the 77 // environment). 78 tmp := Path{ 79 v: p.v.Clone(), 80 } 81 if err = tmp.v.Set(buf.String()); err != nil { 82 return 83 } 84 return tmp, nil 85 } 86 87 // MustPrepend calls p.Prepend and panics if it returns an error. 88 func (p Path) MustPrepend(paths ...string) Path { 89 newpath, err := p.Prepend(paths...) 90 if err != nil { 91 panic(err) 92 } 93 return newpath 94 } 95 96 // Append appends paths to the current value of p. 97 func (p Path) Append(paths ...string) (newpath Path, err error) { 98 defer func() { 99 if err != nil { 100 err = fmt.Errorf("failed to append path(s): %w", err) 101 } 102 }() 103 104 if len(paths) == 0 { 105 return p, nil 106 } 107 108 // Calculate how much extra storage is needed. 109 extra := len(paths) // for delimiters 110 for _, path := range paths { 111 extra += len(path) 112 } 113 114 var buf bytes.Buffer 115 buf.Grow(len(p.v.Value()) + extra) 116 117 // Write the current value before any appended path(s). 118 buf.WriteString(p.v.Value()) //nolint:errcheck 119 120 // Append all of the path fragments to the current value. 121 for _, path := range paths { 122 if len(path) == 0 { 123 continue 124 } 125 126 buf.WriteByte(os.PathListSeparator) //nolint:errcheck 127 buf.WriteString(path) //nolint:errcheck 128 } 129 130 // Create a copy of this Path and persist the change to the copy (and the 131 // environment). 132 tmp := Path{ 133 v: p.v.Clone(), 134 } 135 if err = tmp.v.Set(buf.String()); err != nil { 136 return 137 } 138 return tmp, nil 139 } 140 141 // MustAppend calls p.Append and panics if it returns an error. 142 func (p Path) MustAppend(paths ...string) Path { 143 newpath, err := p.Append(paths...) 144 if err != nil { 145 panic(err) 146 } 147 return newpath 148 } 149 150 // Restore PATH to its original state within the environment. 151 func (p Path) Restore() error { 152 return p.v.Restore() 153 } 154 155 // MustRestore calls p.Restore and panics if it returns an error. 156 func (p Path) MustRestore() { 157 if err := p.Restore(); err != nil { 158 panic(err) 159 } 160 } 161 162 // Value returns the current value of p. 163 func (p Path) Value() string { 164 return p.v.Value() 165 } 166 167 // String returns p as a string. 168 func (p Path) String() string { 169 return p.Value() 170 }