├── LICENSE.md ├── README.md ├── envconf.go ├── envconf_flag_test.go └── envconf_test.go /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ian Kent 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | envconf 2 | ======= 3 | 4 | Configure your Go application from the environment. 5 | 6 | Supports most basic Go types and works nicely with the built in `flag` package. 7 | 8 | ```go 9 | package main 10 | 11 | import( 12 | "flag" 13 | "fmt" 14 | . "github.com/ian-kent/envconf" 15 | ) 16 | 17 | func main() { 18 | count := flag.Int("count", FromEnvP("COUNT", 15).(int), "Count target") 19 | flag.Parse() 20 | for i := 1; i <= *count; i++ { 21 | fmt.Printf("%d\n", i) 22 | } 23 | } 24 | ``` 25 | 26 | ## Licence 27 | 28 | Copyright ©‎ 2014, Ian Kent (http://iankent.uk). 29 | 30 | Released under MIT license, see [LICENSE](LICENSE.md) for details. 31 | -------------------------------------------------------------------------------- /envconf.go: -------------------------------------------------------------------------------- 1 | package envconf 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | // ErrUnsupportedType is returned if the type passed in is unsupported 12 | ErrUnsupportedType = errors.New("Unsupported type") 13 | ) 14 | 15 | // FromEnvP is the same as FromEnv, but panics on error 16 | func FromEnvP(env string, value interface{}) interface{} { 17 | ev, err := FromEnv(env, value) 18 | if err != nil { 19 | panic(err) 20 | } 21 | return ev 22 | } 23 | 24 | // FromEnv returns the environment variable specified by env 25 | // using the type of value 26 | func FromEnv(env string, value interface{}) (interface{}, error) { 27 | envs := os.Environ() 28 | found := false 29 | for _, e := range envs { 30 | if strings.HasPrefix(e, env+"=") { 31 | found = true 32 | break 33 | } 34 | } 35 | 36 | if !found { 37 | return value, nil 38 | } 39 | 40 | ev := os.Getenv(env) 41 | 42 | switch value.(type) { 43 | case string: 44 | vt := interface{}(ev) 45 | return vt, nil 46 | case int: 47 | i, e := strconv.ParseInt(ev, 10, 64) 48 | return int(i), e 49 | case int8: 50 | i, e := strconv.ParseInt(ev, 10, 8) 51 | return int8(i), e 52 | case int16: 53 | i, e := strconv.ParseInt(ev, 10, 16) 54 | return int16(i), e 55 | case int32: 56 | i, e := strconv.ParseInt(ev, 10, 32) 57 | return int32(i), e 58 | case int64: 59 | i, e := strconv.ParseInt(ev, 10, 64) 60 | return i, e 61 | case uint: 62 | i, e := strconv.ParseUint(ev, 10, 64) 63 | return uint(i), e 64 | case uint8: 65 | i, e := strconv.ParseUint(ev, 10, 8) 66 | return uint8(i), e 67 | case uint16: 68 | i, e := strconv.ParseUint(ev, 10, 16) 69 | return uint16(i), e 70 | case uint32: 71 | i, e := strconv.ParseUint(ev, 10, 32) 72 | return uint32(i), e 73 | case uint64: 74 | i, e := strconv.ParseUint(ev, 10, 64) 75 | return i, e 76 | case float32: 77 | i, e := strconv.ParseFloat(ev, 32) 78 | return float32(i), e 79 | case float64: 80 | i, e := strconv.ParseFloat(ev, 64) 81 | return float64(i), e 82 | case bool: 83 | i, e := strconv.ParseBool(ev) 84 | return i, e 85 | default: 86 | return value, ErrUnsupportedType 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /envconf_flag_test.go: -------------------------------------------------------------------------------- 1 | package envconf 2 | 3 | import ( 4 | "flag" 5 | "os" 6 | "testing" 7 | 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | func TestFlagFromEnv(t *testing.T) { 12 | Convey("Replacing flags with env vars after parsing", t, func() { 13 | os.Clearenv() 14 | 15 | s := flag.String("test-flag", "default", "A test flag") 16 | i := flag.Int("test-flag-1", 10, "A test flag") 17 | flag.Parse() 18 | 19 | sv := FromEnvP("TEST_FLAG", *s) 20 | iv := FromEnvP("TEST_FLAG_1", *i) 21 | 22 | So(sv, ShouldEqual, "default") 23 | So(iv, ShouldEqual, 10) 24 | 25 | os.Setenv("TEST_FLAG", "foo") 26 | os.Setenv("TEST_FLAG_1", "15") 27 | 28 | sv = FromEnvP("TEST_FLAG", *s) 29 | iv = FromEnvP("TEST_FLAG_1", *i) 30 | 31 | So(sv, ShouldEqual, "foo") 32 | So(iv, ShouldEqual, 15) 33 | }) 34 | 35 | Convey("Replacing flags with env vars after parsing", t, func() { 36 | os.Clearenv() 37 | 38 | s := flag.String("test-flag-2", FromEnvP("TEST_FLAG_2", "default").(string), "A test flag") 39 | i := flag.Int("test-flag-3", FromEnvP("TEST_FLAG_3", 10).(int), "A test flag") 40 | flag.Parse() 41 | 42 | So(*s, ShouldEqual, "default") 43 | So(*i, ShouldEqual, 10) 44 | 45 | os.Setenv("TEST_FLAG_4", "foo") 46 | os.Setenv("TEST_FLAG_5", "15") 47 | 48 | s = flag.String("test-flag-4", FromEnvP("TEST_FLAG_4", "default").(string), "A test flag") 49 | i = flag.Int("test-flag-5", FromEnvP("TEST_FLAG_5", 10).(int), "A test flag") 50 | flag.Parse() 51 | 52 | So(*s, ShouldEqual, "foo") 53 | So(*i, ShouldEqual, 15) 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /envconf_test.go: -------------------------------------------------------------------------------- 1 | package envconf 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | . "github.com/smartystreets/goconvey/convey" 8 | ) 9 | 10 | func TestFromEnv(t *testing.T) { 11 | Convey("Value not replaced if environment variable not set", t, func() { 12 | os.Clearenv() 13 | v := interface{}("default") 14 | 15 | vl, err := FromEnv("FOO_VAR", v) 16 | So(vl, ShouldEqual, "default") 17 | So(err, ShouldBeNil) 18 | }) 19 | 20 | Convey("Value replaced if environment variable is set", t, func() { 21 | os.Clearenv() 22 | v := interface{}("default") 23 | os.Setenv("FOO_VAR", "bar") 24 | 25 | vl, err := FromEnv("FOO_VAR", v) 26 | So(vl, ShouldEqual, "bar") 27 | So(err, ShouldBeNil) 28 | }) 29 | 30 | Convey("Value replaced if environment variable is set to empty value", t, func() { 31 | os.Clearenv() 32 | v := interface{}("default") 33 | os.Setenv("FOO_VAR", "") 34 | 35 | vl, err := FromEnv("FOO_VAR", v) 36 | So(vl, ShouldEqual, "") 37 | So(err, ShouldBeNil) 38 | }) 39 | 40 | Convey("FromEnvP panics on error", t, func() { 41 | os.Clearenv() 42 | v := interface{}(map[string]string{"foo": "bar"}) 43 | os.Setenv("FOO_VAR", "true") 44 | 45 | So(func() { 46 | FromEnvP("FOO_VAR", v) 47 | }, ShouldPanic) 48 | }) 49 | 50 | Convey("Unsupported type returns an error", t, func() { 51 | os.Clearenv() 52 | v := interface{}(map[string]string{"foo": "bar"}) 53 | 54 | os.Setenv("FOO_VAR", "true") 55 | vl, err := FromEnv("FOO_VAR", v) 56 | So(vl, ShouldEqual, v) 57 | So(err, ShouldEqual, ErrUnsupportedType) 58 | }) 59 | 60 | Convey("bool returns a bool", t, func() { 61 | os.Clearenv() 62 | v := interface{}(false) 63 | 64 | vl, err := FromEnv("FOO_VAR", v) 65 | So(vl, ShouldEqual, false) 66 | So(err, ShouldBeNil) 67 | 68 | os.Setenv("FOO_VAR", "true") 69 | vl, err = FromEnv("FOO_VAR", v) 70 | So(vl, ShouldEqual, true) 71 | So(vl, ShouldHaveSameTypeAs, false) 72 | So(vl, ShouldNotEqual, "test") 73 | So(err, ShouldBeNil) 74 | }) 75 | 76 | Convey("string returns a string", t, func() { 77 | os.Clearenv() 78 | v := interface{}(string("test")) 79 | 80 | vl, err := FromEnv("FOO_VAR", v) 81 | So(vl, ShouldEqual, string("test")) 82 | So(err, ShouldBeNil) 83 | 84 | os.Setenv("FOO_VAR", "bar") 85 | vl, err = FromEnv("FOO_VAR", v) 86 | So(vl, ShouldEqual, string("bar")) 87 | So(vl, ShouldHaveSameTypeAs, string("bar")) 88 | So(vl, ShouldNotEqual, "test") 89 | So(err, ShouldBeNil) 90 | }) 91 | 92 | Convey("int returns an int", t, func() { 93 | os.Clearenv() 94 | v := interface{}(int(10)) 95 | 96 | vl, err := FromEnv("FOO_VAR", v) 97 | So(vl, ShouldEqual, int(10)) 98 | So(err, ShouldBeNil) 99 | 100 | os.Setenv("FOO_VAR", "15") 101 | vl, err = FromEnv("FOO_VAR", v) 102 | So(vl, ShouldEqual, int(15)) 103 | So(vl, ShouldHaveSameTypeAs, int(15)) 104 | So(vl, ShouldNotEqual, "15") 105 | So(err, ShouldBeNil) 106 | }) 107 | 108 | Convey("int8 returns an int8", t, func() { 109 | os.Clearenv() 110 | v := interface{}(int8(10)) 111 | 112 | vl, err := FromEnv("FOO_VAR", v) 113 | So(vl, ShouldEqual, int8(10)) 114 | So(err, ShouldBeNil) 115 | 116 | os.Setenv("FOO_VAR", "15") 117 | vl, err = FromEnv("FOO_VAR", v) 118 | So(vl, ShouldEqual, int8(15)) 119 | So(vl, ShouldHaveSameTypeAs, int8(15)) 120 | So(vl, ShouldNotEqual, "15") 121 | So(err, ShouldBeNil) 122 | }) 123 | 124 | Convey("int16 returns an int16", t, func() { 125 | os.Clearenv() 126 | v := interface{}(int16(10)) 127 | 128 | vl, err := FromEnv("FOO_VAR", v) 129 | So(vl, ShouldEqual, int16(10)) 130 | So(err, ShouldBeNil) 131 | 132 | os.Setenv("FOO_VAR", "15") 133 | vl, err = FromEnv("FOO_VAR", v) 134 | So(vl, ShouldEqual, int16(15)) 135 | So(vl, ShouldHaveSameTypeAs, int16(15)) 136 | So(vl, ShouldNotEqual, "15") 137 | So(err, ShouldBeNil) 138 | }) 139 | 140 | Convey("int32 returns an int32", t, func() { 141 | os.Clearenv() 142 | v := interface{}(int32(10)) 143 | 144 | vl, err := FromEnv("FOO_VAR", v) 145 | So(vl, ShouldEqual, int32(10)) 146 | So(err, ShouldBeNil) 147 | 148 | os.Setenv("FOO_VAR", "15") 149 | vl, err = FromEnv("FOO_VAR", v) 150 | So(vl, ShouldEqual, int32(15)) 151 | So(vl, ShouldHaveSameTypeAs, int32(15)) 152 | So(vl, ShouldNotEqual, "15") 153 | So(err, ShouldBeNil) 154 | }) 155 | 156 | Convey("int64 returns an int64", t, func() { 157 | os.Clearenv() 158 | v := interface{}(int64(10)) 159 | 160 | vl, err := FromEnv("FOO_VAR", v) 161 | So(vl, ShouldEqual, int64(10)) 162 | So(err, ShouldBeNil) 163 | 164 | os.Setenv("FOO_VAR", "15") 165 | vl, err = FromEnv("FOO_VAR", v) 166 | So(vl, ShouldEqual, int64(15)) 167 | So(vl, ShouldHaveSameTypeAs, int64(15)) 168 | So(vl, ShouldNotEqual, "15") 169 | So(err, ShouldBeNil) 170 | }) 171 | 172 | Convey("uint returns a uint", t, func() { 173 | os.Clearenv() 174 | v := interface{}(uint(10)) 175 | 176 | vl, err := FromEnv("FOO_VAR", v) 177 | So(vl, ShouldEqual, uint(10)) 178 | So(err, ShouldBeNil) 179 | 180 | os.Setenv("FOO_VAR", "15") 181 | vl, err = FromEnv("FOO_VAR", v) 182 | So(vl, ShouldEqual, uint(15)) 183 | So(vl, ShouldHaveSameTypeAs, uint(15)) 184 | So(vl, ShouldNotEqual, "15") 185 | So(err, ShouldBeNil) 186 | }) 187 | 188 | Convey("uint8 returns a uint8", t, func() { 189 | os.Clearenv() 190 | v := interface{}(uint8(10)) 191 | 192 | vl, err := FromEnv("FOO_VAR", v) 193 | So(vl, ShouldEqual, uint8(10)) 194 | So(err, ShouldBeNil) 195 | 196 | os.Setenv("FOO_VAR", "15") 197 | vl, err = FromEnv("FOO_VAR", v) 198 | So(vl, ShouldEqual, uint8(15)) 199 | So(vl, ShouldHaveSameTypeAs, uint8(15)) 200 | So(vl, ShouldNotEqual, "15") 201 | So(err, ShouldBeNil) 202 | }) 203 | 204 | Convey("uint16 returns a uint16", t, func() { 205 | os.Clearenv() 206 | v := interface{}(uint16(10)) 207 | 208 | vl, err := FromEnv("FOO_VAR", v) 209 | So(vl, ShouldEqual, uint16(10)) 210 | So(err, ShouldBeNil) 211 | 212 | os.Setenv("FOO_VAR", "15") 213 | vl, err = FromEnv("FOO_VAR", v) 214 | So(vl, ShouldEqual, uint16(15)) 215 | So(vl, ShouldHaveSameTypeAs, uint16(15)) 216 | So(vl, ShouldNotEqual, "15") 217 | So(err, ShouldBeNil) 218 | }) 219 | 220 | Convey("uint32 returns a uint32", t, func() { 221 | os.Clearenv() 222 | v := interface{}(uint32(10)) 223 | 224 | vl, err := FromEnv("FOO_VAR", v) 225 | So(vl, ShouldEqual, uint32(10)) 226 | So(err, ShouldBeNil) 227 | 228 | os.Setenv("FOO_VAR", "15") 229 | vl, err = FromEnv("FOO_VAR", v) 230 | So(vl, ShouldEqual, uint32(15)) 231 | So(vl, ShouldHaveSameTypeAs, uint32(15)) 232 | So(vl, ShouldNotEqual, "15") 233 | So(err, ShouldBeNil) 234 | }) 235 | 236 | Convey("uint64 returns a uint64", t, func() { 237 | os.Clearenv() 238 | v := interface{}(uint64(10)) 239 | 240 | vl, err := FromEnv("FOO_VAR", v) 241 | So(vl, ShouldEqual, uint64(10)) 242 | So(err, ShouldBeNil) 243 | 244 | os.Setenv("FOO_VAR", "15") 245 | vl, err = FromEnv("FOO_VAR", v) 246 | So(vl, ShouldEqual, uint64(15)) 247 | So(vl, ShouldHaveSameTypeAs, uint64(15)) 248 | So(vl, ShouldNotEqual, "15") 249 | So(err, ShouldBeNil) 250 | }) 251 | 252 | Convey("float32 returns a float32", t, func() { 253 | os.Clearenv() 254 | v := interface{}(float32(10)) 255 | 256 | vl, err := FromEnv("FOO_VAR", v) 257 | So(vl, ShouldEqual, float32(10)) 258 | So(err, ShouldBeNil) 259 | 260 | os.Setenv("FOO_VAR", "15") 261 | vl, err = FromEnv("FOO_VAR", v) 262 | So(vl, ShouldEqual, float32(15)) 263 | So(vl, ShouldHaveSameTypeAs, float32(15)) 264 | So(vl, ShouldNotEqual, "15") 265 | So(err, ShouldBeNil) 266 | }) 267 | 268 | Convey("float64 returns a float64", t, func() { 269 | os.Clearenv() 270 | v := interface{}(float64(10)) 271 | 272 | vl, err := FromEnv("FOO_VAR", v) 273 | So(vl, ShouldEqual, float64(10)) 274 | So(err, ShouldBeNil) 275 | 276 | os.Setenv("FOO_VAR", "15") 277 | vl, err = FromEnv("FOO_VAR", v) 278 | So(vl, ShouldEqual, float64(15)) 279 | So(vl, ShouldHaveSameTypeAs, float64(15)) 280 | So(vl, ShouldNotEqual, "15") 281 | So(err, ShouldBeNil) 282 | }) 283 | } 284 | --------------------------------------------------------------------------------