|
3 | 3 | <p align="center">A simple, zero-dependencies library to parse environment variables into structs.</p>
|
4 | 4 | </p>
|
5 | 5 |
|
| 6 | +A simple and zero-dependencies library to parse environment variables into `struct`s. |
| 7 | + |
| 8 | +###### Getting started |
| 9 | + |
| 10 | +```go |
| 11 | +type config struct { |
| 12 | + Home string `env:"HOME"` |
| 13 | +} |
| 14 | + |
| 15 | +// parse |
| 16 | +var cfg config |
| 17 | +err := env.Parse(&cfg) |
| 18 | + |
| 19 | +// parse with generics |
| 20 | +cfg, err := env.ParseAs[config]() |
| 21 | +``` |
| 22 | + |
| 23 | +You can see the full documentation and list of examples at |
| 24 | +[pkg.go.dev](https://pkg.go.dev/github.com/caarlos0/env/v11). |
| 25 | + |
6 | 26 | ---
|
7 | 27 |
|
8 | 28 | ## Used and supported by
|
|
17 | 37 | <br/>
|
18 | 38 | </p>
|
19 | 39 |
|
20 |
| -## Example |
21 |
| - |
22 |
| -Get the module with: |
23 |
| - |
24 |
| -```sh |
25 |
| -go get github.com/caarlos0/env/v11 |
26 |
| -``` |
27 |
| - |
28 |
| -The usage looks like this: |
29 |
| - |
30 |
| -```go |
31 |
| -package main |
| 40 | +## Usage |
32 | 41 |
|
33 |
| -import ( |
34 |
| - "fmt" |
35 |
| - "time" |
36 |
| - |
37 |
| - "github.com/caarlos0/env/v11" |
38 |
| -) |
39 |
| - |
40 |
| -type config struct { |
41 |
| - Home string `env:"HOME"` |
42 |
| - Port int `env:"PORT" envDefault:"3000"` |
43 |
| - Password string `env:"PASSWORD,unset"` |
44 |
| - IsProduction bool `env:"PRODUCTION"` |
45 |
| - Duration time.Duration `env:"DURATION"` |
46 |
| - Hosts []string `env:"HOSTS" envSeparator:":"` |
47 |
| - TempFolder string `env:"TEMP_FOLDER,expand" envDefault:"${HOME}/tmp"` |
48 |
| - StringInts map[string]int `env:"MAP_STRING_INT"` |
49 |
| -} |
50 |
| - |
51 |
| -func main() { |
52 |
| - cfg := config{} |
53 |
| - if err := env.Parse(&cfg); err != nil { |
54 |
| - fmt.Printf("%+v\n", err) |
55 |
| - } |
56 |
| - |
57 |
| - // or you can use generics |
58 |
| - cfg, err := env.ParseAs[config]() |
59 |
| - if err != nil { |
60 |
| - fmt.Printf("%+v\n", err) |
61 |
| - } |
62 |
| - |
63 |
| - fmt.Printf("%+v\n", cfg) |
64 |
| -} |
65 |
| -``` |
66 |
| - |
67 |
| -You can run it like this: |
68 |
| - |
69 |
| -```sh |
70 |
| -$ PRODUCTION=true HOSTS="host1:host2:host3" DURATION=1s MAP_STRING_INT=k1:1,k2:2 go run main.go |
71 |
| -{Home:/your/home Port:3000 IsProduction:true Hosts:[host1 host2 host3] Duration:1s StringInts:map[k1:1 k2:2]} |
72 |
| -``` |
73 |
| - |
74 |
| -## Caveats |
| 42 | +### Caveats |
75 | 43 |
|
76 | 44 | > [!CAUTION]
|
77 | 45 | >
|
78 | 46 | > _Unexported fields_ will be **ignored** by `env`.
|
79 | 47 | > This is by design and will not change.
|
80 | 48 |
|
81 |
| -## Supported types and defaults |
| 49 | +### Methods |
| 50 | + |
| 51 | +- `Parse`: parse the current environment into a type |
| 52 | +- `ParseAs`: parse the current environment into a type using generics |
| 53 | +- `ParseWithOptions`: parse the current environment into a type with custom |
| 54 | + options |
| 55 | +- `ParseAsithOptions`: parse the current environment into a type with custom |
| 56 | + options and using generics |
| 57 | +- `Must`: can be used to wrap `Parse.*` calls to panic on error |
| 58 | +- `GetFieldParams`: get the `env` parsed options for a type |
| 59 | +- `GetFieldParamsWithOptions`: get the `env` parsed options for a type with |
| 60 | + custom options |
| 61 | + |
| 62 | +### Supported types and defaults |
82 | 63 |
|
83 | 64 | Out of the box all built-in types are supported, plus a few others that
|
84 | 65 | are commonly used.
|
85 | 66 |
|
86 | 67 | Complete list:
|
87 | 68 |
|
88 |
| -- `string` |
89 | 69 | - `bool`
|
90 |
| -- `int` |
91 |
| -- `int8` |
| 70 | +- `float32` |
| 71 | +- `float64` |
92 | 72 | - `int16`
|
93 | 73 | - `int32`
|
94 | 74 | - `int64`
|
95 |
| -- `uint` |
96 |
| -- `uint8` |
| 75 | +- `int8` |
| 76 | +- `int` |
| 77 | +- `string` |
97 | 78 | - `uint16`
|
98 | 79 | - `uint32`
|
99 | 80 | - `uint64`
|
100 |
| -- `float32` |
101 |
| -- `float64` |
| 81 | +- `uint8` |
| 82 | +- `uint` |
102 | 83 | - `time.Duration`
|
103 | 84 | - `encoding.TextUnmarshaler`
|
104 | 85 | - `url.URL`
|
105 | 86 |
|
106 | 87 | Pointers, slices and slices of pointers, and maps of those types are also
|
107 | 88 | supported.
|
108 | 89 |
|
109 |
| -You can also use/define a [custom parser func](#custom-parser-funcs) for any |
110 |
| -other type you want. |
111 |
| - |
112 |
| -You can also use custom keys and values in your maps, as long as you provide a |
113 |
| -parser function for them. |
114 |
| - |
115 |
| -If you set the `envDefault` tag for something, this value will be used in the |
116 |
| -case of absence of it in the environment. |
117 |
| - |
118 |
| -By default, slice types will split the environment value on `,`; you can change |
119 |
| -this behavior by setting the `envSeparator` tag. For map types, the default |
120 |
| -separator between key and value is `:` and `,` for key-value pairs. |
121 |
| -The behavior can be changed by setting the `envKeyValSeparator` and |
122 |
| -`envSeparator` tags accordingly. |
123 |
| - |
124 |
| -## Custom Parser Funcs |
125 |
| - |
126 |
| -If you have a type that is not supported out of the box by the lib, you are able |
127 |
| -to use (or define) and pass custom parsers (and their associated `reflect.Type`) |
128 |
| -to the `env.ParseWithOptions()` function. |
129 |
| - |
130 |
| -In addition to accepting a struct pointer (same as `Parse()`), this function |
131 |
| -also accepts a `Options{}`, and you can set your custom parsers in the `FuncMap` |
132 |
| -field. |
133 |
| - |
134 |
| -If you add a custom parser for, say `Foo`, it will also be used to parse |
135 |
| -`*Foo` and `[]Foo` types. |
136 |
| - |
137 |
| -Check the examples in the [go doc](http://pkg.go.dev/github.com/caarlos0/env/v11) |
138 |
| -for more info. |
139 |
| - |
140 |
| -### A note about `TextUnmarshaler` and `time.Time` |
141 |
| - |
142 |
| -Env supports by default anything that implements the `TextUnmarshaler` interface. |
143 |
| -That includes things like `time.Time` for example. |
144 |
| -The upside is that depending on the format you need, you don't need to change |
145 |
| -anything. |
146 |
| -The downside is that if you do need time in another format, you'll need to |
147 |
| -create your own type. |
148 |
| - |
149 |
| -Its fairly straightforward: |
150 |
| - |
151 |
| -```go |
152 |
| -type MyTime time.Time |
153 |
| - |
154 |
| -func (t *MyTime) UnmarshalText(text []byte) error { |
155 |
| - tt, err := time.Parse("2006-01-02", string(text)) |
156 |
| - *t = MyTime(tt) |
157 |
| - return err |
158 |
| -} |
159 |
| - |
160 |
| -type Config struct { |
161 |
| - SomeTime MyTime `env:"SOME_TIME"` |
162 |
| -} |
163 |
| -``` |
164 |
| - |
165 |
| -And then you can parse `Config` with `env.Parse`. |
166 |
| - |
167 |
| -## Required fields |
| 90 | +You may also add custom parsers for your types. |
168 | 91 |
|
169 |
| -The `env` tag option `required` (e.g., `env:"tagKey,required"`) can be added to |
170 |
| -ensure that some environment variable is set. In the example above, an error is |
171 |
| -returned if the `config` struct is changed to: |
172 |
| - |
173 |
| -```go |
174 |
| -type config struct { |
175 |
| - SecretKey string `env:"SECRET_KEY,required"` |
176 |
| -} |
177 |
| -``` |
| 92 | +### Tags |
178 | 93 |
|
179 |
| -> [!NOTE] |
180 |
| -> |
181 |
| -> Note that being set is not the same as being empty. |
182 |
| -> If the variable is set, but empty, the field will have its type's default |
183 |
| -> value. |
184 |
| -> This also means that custom parser funcs will not be invoked. |
| 94 | +The following tags are provided: |
185 | 95 |
|
186 |
| -## Expand vars |
| 96 | +- `env`: sets the environment variable name and optionally takes the tag options |
| 97 | + described below |
| 98 | +- `envDefault`: sets the default value for the field |
| 99 | +- `envPrefix`: can be used in a field that is a complex type to set a prefix to |
| 100 | + all environment variables used in it |
| 101 | +- `envSeparator`: sets the character to be used to separate items in slices and |
| 102 | + maps (default: `,`) |
| 103 | +- `envKeyValSeparator`: sets the character to be used to separate keys and their |
| 104 | + values in maps (default: `:`) |
187 | 105 |
|
188 |
| -If you set the `expand` option, environment variables (either in `${var}` or |
189 |
| -`$var` format) in the string will be replaced according with the actual value |
190 |
| -of the variable. For example: |
| 106 | +### `env` tag options |
191 | 107 |
|
192 |
| -```go |
193 |
| -type config struct { |
194 |
| - SecretKey string `env:"SECRET_KEY,expand"` |
195 |
| -} |
196 |
| -``` |
| 108 | +Here are all the options available for the `env` tag: |
197 | 109 |
|
198 |
| -This also works with `envDefault`: |
| 110 | +- `,expand`: expands environment variables, e.g. `FOO_${BAR}` |
| 111 | +- `,file`: instructs that the content of the variable is a path to a file that should be read |
| 112 | +- `,init`: initialize nil pointers |
| 113 | +- `,notEmpty`: make the field errors if the environment variable is empty |
| 114 | +- `,required`: make the field errors if the environment variable is not set |
| 115 | +- `,unset`: unset the environment variable after use |
199 | 116 |
|
200 |
| -```go |
201 |
| -import ( |
202 |
| - "fmt" |
203 |
| - "github.com/caarlos0/env/v11" |
204 |
| -) |
| 117 | +### Parse Options |
205 | 118 |
|
206 |
| -type config struct { |
207 |
| - Host string `env:"HOST" envDefault:"localhost"` |
208 |
| - Port int `env:"PORT" envDefault:"3000"` |
209 |
| - Address string `env:"ADDRESS,expand" envDefault:"$HOST:${PORT}"` |
210 |
| -} |
211 |
| - |
212 |
| -func main() { |
213 |
| - cfg := config{} |
214 |
| - if err := env.Parse(&cfg); err != nil { |
215 |
| - fmt.Printf("%+v\n", err) |
216 |
| - } |
217 |
| - fmt.Printf("%+v\n", cfg) |
218 |
| -} |
219 |
| -``` |
220 |
| - |
221 |
| -results in this: |
222 |
| - |
223 |
| -```sh |
224 |
| -$ PORT=8080 go run main.go |
225 |
| -{Host:localhost Port:8080 Address:localhost:8080} |
226 |
| -``` |
227 |
| - |
228 |
| -## Init `nil` pointers |
229 |
| - |
230 |
| -You can automatically initialize `nil` pointers regardless of if a variable is |
231 |
| -set for them or not. |
232 |
| -This behavior can be enabled by using the `init` tag option. |
233 |
| - |
234 |
| -Example: |
235 |
| - |
236 |
| -```go |
237 |
| -type config struct { |
238 |
| - URL *url.URL `env:"URL,init"` |
239 |
| -} |
240 |
| -``` |
| 119 | +There are a few options available in the functions that end with `WithOptions`: |
241 | 120 |
|
242 |
| -## Not Empty fields |
| 121 | +- `Environment`: keys and values to be used instead of `os.Environ()` |
| 122 | +- `TagName`: specifies another tag name to use rather than the default `env` |
| 123 | +- `RequiredIfNoDef`: set all `env` fields as required if they do not declare |
| 124 | + `envDefault` |
| 125 | +- `OnSet`: allows to hook into the `env` parsing and do something when a value |
| 126 | + is set |
| 127 | +- `Prefix`: prefix to be used in all environment variables |
| 128 | +- `UseFieldNameByDefault`: defines whether or not `env` should use the field name by default if the `env` key is missing |
| 129 | +- `FuncMap`: custom parse functions for custom types |
243 | 130 |
|
244 |
| -While `required` demands the environment variable to be set, it doesn't check |
245 |
| -its value. If you want to make sure the environment is set and not empty, you |
246 |
| -need to use the `notEmpty` tag option instead (`env:"SOME_ENV,notEmpty"`). |
247 |
| - |
248 |
| -Example: |
249 |
| - |
250 |
| -```go |
251 |
| -type config struct { |
252 |
| - SecretKey string `env:"SECRET_KEY,notEmpty"` |
253 |
| -} |
254 |
| -``` |
255 |
| - |
256 |
| -## Unset environment variable after reading it |
257 |
| - |
258 |
| -The `env` tag option `unset` (e.g., `env:"tagKey,unset"`) can be added |
259 |
| -to ensure that some environment variable is unset after reading it. |
260 |
| - |
261 |
| -Example: |
262 |
| - |
263 |
| -```go |
264 |
| -type config struct { |
265 |
| - SecretKey string `env:"SECRET_KEY,unset"` |
266 |
| -} |
267 |
| -``` |
268 |
| - |
269 |
| -## From file |
270 |
| - |
271 |
| -The `env` tag option `file` (e.g., `env:"tagKey,file"`) can be added |
272 |
| -in order to indicate that the value of the variable shall be loaded from a |
273 |
| -file. |
274 |
| -The path of that file is given by the environment variable associated with it: |
275 |
| - |
276 |
| -```go |
277 |
| -package main |
278 |
| - |
279 |
| -import ( |
280 |
| - "fmt" |
281 |
| - "time" |
282 |
| - |
283 |
| - "github.com/caarlos0/env/v11" |
284 |
| -) |
285 |
| - |
286 |
| -type config struct { |
287 |
| - Secret string `env:"SECRET,file"` |
288 |
| - Password string `env:"PASSWORD,file" envDefault:"/tmp/password"` |
289 |
| - Certificate string `env:"CERTIFICATE,file,expand" envDefault:"${CERTIFICATE_FILE}"` |
290 |
| -} |
291 |
| - |
292 |
| -func main() { |
293 |
| - cfg := config{} |
294 |
| - if err := env.Parse(&cfg); err != nil { |
295 |
| - fmt.Printf("%+v\n", err) |
296 |
| - } |
297 |
| - |
298 |
| - fmt.Printf("%+v\n", cfg) |
299 |
| -} |
300 |
| -``` |
301 |
| - |
302 |
| -```sh |
303 |
| -$ echo qwerty > /tmp/secret |
304 |
| -$ echo dvorak > /tmp/password |
305 |
| -$ echo coleman > /tmp/certificate |
306 |
| - |
307 |
| -$ SECRET=/tmp/secret \ |
308 |
| - CERTIFICATE_FILE=/tmp/certificate \ |
309 |
| - go run main.go |
310 |
| -{Secret:qwerty Password:dvorak Certificate:coleman} |
311 |
| -``` |
| 131 | +### Documentation and examples |
312 | 132 |
|
313 |
| -## Options |
314 |
| - |
315 |
| -### Use field names as environment variables by default |
316 |
| - |
317 |
| -If you don't want to set the `env` tag on every field, you can use the |
318 |
| -`UseFieldNameByDefault` option. |
319 |
| - |
320 |
| -It will use the field name to define the environment variable name. |
321 |
| -So, `Foo` becomes `FOO`, `FooBar` becomes `FOO_BAR`, and so on. |
322 |
| - |
323 |
| -Here's an example: |
324 |
| - |
325 |
| -```go |
326 |
| -package main |
327 |
| - |
328 |
| -import ( |
329 |
| - "fmt" |
330 |
| - "log" |
331 |
| - |
332 |
| - "github.com/caarlos0/env/v11" |
333 |
| -) |
334 |
| - |
335 |
| -type Config struct { |
336 |
| - Username string // will use $USERNAME |
337 |
| - Password string // will use $PASSWORD |
338 |
| - UserFullName string // will use $USER_FULL_NAME |
339 |
| -} |
340 |
| - |
341 |
| -func main() { |
342 |
| - cfg := &Config{} |
343 |
| - opts := env.Options{UseFieldNameByDefault: true} |
344 |
| - |
345 |
| - // Load env vars. |
346 |
| - if err := env.ParseWithOptions(cfg, opts); err != nil { |
347 |
| - log.Fatal(err) |
348 |
| - } |
349 |
| - |
350 |
| - // Print the loaded data. |
351 |
| - fmt.Printf("%+v\n", cfg) |
352 |
| -} |
353 |
| -``` |
354 |
| - |
355 |
| -### Environment |
356 |
| - |
357 |
| -By setting the `Options.Environment` map you can tell `Parse` to add those |
358 |
| -`keys` and `values` as `env` vars before parsing is done. |
359 |
| -These `envs` are stored in the map and never actually set by `os.Setenv`. |
360 |
| -This option effectively makes `env` ignore the OS environment variables: only |
361 |
| -the ones provided in the option are used. |
362 |
| - |
363 |
| -This can make your testing scenarios a bit more clean and easy to handle. |
364 |
| - |
365 |
| -```go |
366 |
| -package main |
367 |
| - |
368 |
| -import ( |
369 |
| - "fmt" |
370 |
| - "log" |
371 |
| - |
372 |
| - "github.com/caarlos0/env/v11" |
373 |
| -) |
374 |
| - |
375 |
| -type Config struct { |
376 |
| - Password string `env:"PASSWORD"` |
377 |
| -} |
378 |
| - |
379 |
| -func main() { |
380 |
| - cfg := &Config{} |
381 |
| - opts := env.Options{Environment: map[string]string{ |
382 |
| - "PASSWORD": "MY_PASSWORD", |
383 |
| - }} |
384 |
| - |
385 |
| - // Load env vars. |
386 |
| - if err := env.ParseWithOptions(cfg, opts); err != nil { |
387 |
| - log.Fatal(err) |
388 |
| - } |
389 |
| - |
390 |
| - // Print the loaded data. |
391 |
| - fmt.Printf("%+v\n", cfg) |
392 |
| -} |
393 |
| -``` |
394 |
| - |
395 |
| -### Changing default tag name |
396 |
| - |
397 |
| -You can change what tag name to use for setting the env vars by setting the |
398 |
| -`Options.TagName` variable. |
399 |
| - |
400 |
| -For example |
401 |
| - |
402 |
| -```go |
403 |
| -package main |
404 |
| - |
405 |
| -import ( |
406 |
| - "fmt" |
407 |
| - "log" |
408 |
| - |
409 |
| - "github.com/caarlos0/env/v11" |
410 |
| -) |
411 |
| - |
412 |
| -type Config struct { |
413 |
| - Password string `json:"PASSWORD"` |
414 |
| -} |
415 |
| - |
416 |
| -func main() { |
417 |
| - cfg := &Config{} |
418 |
| - opts := env.Options{TagName: "json"} |
419 |
| - |
420 |
| - // Load env vars. |
421 |
| - if err := env.ParseWithOptions(cfg, opts); err != nil { |
422 |
| - log.Fatal(err) |
423 |
| - } |
424 |
| - |
425 |
| - // Print the loaded data. |
426 |
| - fmt.Printf("%+v\n", cfg) |
427 |
| -} |
428 |
| -``` |
429 |
| - |
430 |
| -### Prefixes |
431 |
| - |
432 |
| -You can prefix sub-structs env tags, as well as a whole `env.Parse` call. |
433 |
| - |
434 |
| -Here's an example flexing it a bit: |
435 |
| - |
436 |
| -```go |
437 |
| -package main |
438 |
| - |
439 |
| -import ( |
440 |
| - "fmt" |
441 |
| - "log" |
442 |
| - |
443 |
| - "github.com/caarlos0/env/v11" |
444 |
| -) |
445 |
| - |
446 |
| -type Config struct { |
447 |
| - Home string `env:"HOME"` |
448 |
| -} |
449 |
| - |
450 |
| -type ComplexConfig struct { |
451 |
| - Foo Config `envPrefix:"FOO_"` |
452 |
| - Clean Config |
453 |
| - Bar Config `envPrefix:"BAR_"` |
454 |
| - Blah string `env:"BLAH"` |
455 |
| -} |
456 |
| - |
457 |
| -func main() { |
458 |
| - cfg := &ComplexConfig{} |
459 |
| - opts := env.Options{ |
460 |
| - Prefix: "T_", |
461 |
| - Environment: map[string]string{ |
462 |
| - "T_FOO_HOME": "/foo", |
463 |
| - "T_BAR_HOME": "/bar", |
464 |
| - "T_BLAH": "blahhh", |
465 |
| - "T_HOME": "/clean", |
466 |
| - }, |
467 |
| - } |
468 |
| - |
469 |
| - // Load env vars. |
470 |
| - if err := env.ParseWithOptions(cfg, opts); err != nil { |
471 |
| - log.Fatal(err) |
472 |
| - } |
473 |
| - |
474 |
| - // Print the loaded data. |
475 |
| - fmt.Printf("%+v\n", cfg) |
476 |
| -} |
477 |
| -``` |
478 |
| - |
479 |
| -### Complex objects inside array (slice) |
480 |
| - |
481 |
| -You can set sub-struct field values inside a slice by naming the environment variables with sequential numbers starting from 0 (without omitting numbers in between) and an underscore. |
482 |
| -It is possible to use prefix tag too. |
483 |
| - |
484 |
| -Here's an example with and without prefix tag: |
485 |
| - |
486 |
| -```go |
487 |
| -package main |
488 |
| - |
489 |
| -import ( |
490 |
| - "fmt" |
491 |
| - "log" |
492 |
| - |
493 |
| - "github.com/caarlos0/env/v11" |
494 |
| -) |
495 |
| - |
496 |
| -type Test struct { |
497 |
| - Str string `env:"STR"` |
498 |
| - Num int `env:"NUM"` |
499 |
| -} |
500 |
| -type ComplexConfig struct { |
501 |
| - Baz []Test `env:",init"` |
502 |
| - Bar []Test `envPrefix:"BAR"` |
503 |
| - Foo *[]Test `envPrefix:"FOO_"` |
504 |
| -} |
505 |
| - |
506 |
| -func main() { |
507 |
| - cfg := &ComplexConfig{} |
508 |
| - opts := env.Options{ |
509 |
| - Environment: map[string]string{ |
510 |
| - "0_STR": "bt", |
511 |
| - "1_NUM": "10", |
512 |
| - |
513 |
| - "FOO_0_STR": "b0t", |
514 |
| - "FOO_1_STR": "b1t", |
515 |
| - "FOO_1_NUM": "212", |
516 |
| - |
517 |
| - "BAR_0_STR": "f0t", |
518 |
| - "BAR_0_NUM": "101", |
519 |
| - "BAR_1_STR": "f1t", |
520 |
| - "BAR_1_NUM": "111", |
521 |
| - }, |
522 |
| - } |
523 |
| - |
524 |
| - // Load env vars. |
525 |
| - if err := env.ParseWithOptions(cfg, opts); err != nil { |
526 |
| - log.Fatal(err) |
527 |
| - } |
528 |
| - |
529 |
| - // Print the loaded data. |
530 |
| - fmt.Printf("%+v\n", cfg) |
531 |
| -} |
532 |
| -``` |
533 |
| - |
534 |
| -### On set hooks |
535 |
| - |
536 |
| -You might want to listen to value sets and, for example, log something or do |
537 |
| -some other kind of logic. |
538 |
| -You can do this by passing a `OnSet` option: |
539 |
| - |
540 |
| -```go |
541 |
| -package main |
542 |
| - |
543 |
| -import ( |
544 |
| - "fmt" |
545 |
| - "log" |
546 |
| - |
547 |
| - "github.com/caarlos0/env/v11" |
548 |
| -) |
549 |
| - |
550 |
| -type Config struct { |
551 |
| - Username string `env:"USERNAME" envDefault:"admin"` |
552 |
| - Password string `env:"PASSWORD"` |
553 |
| -} |
554 |
| - |
555 |
| -func main() { |
556 |
| - cfg := &Config{} |
557 |
| - opts := env.Options{ |
558 |
| - OnSet: func(tag string, value interface{}, isDefault bool) { |
559 |
| - fmt.Printf("Set %s to %v (default? %v)\n", tag, value, isDefault) |
560 |
| - }, |
561 |
| - } |
562 |
| - |
563 |
| - // Load env vars. |
564 |
| - if err := env.ParseWithOptions(cfg, opts); err != nil { |
565 |
| - log.Fatal(err) |
566 |
| - } |
567 |
| - |
568 |
| - // Print the loaded data. |
569 |
| - fmt.Printf("%+v\n", cfg) |
570 |
| -} |
571 |
| -``` |
572 |
| - |
573 |
| -## Making all fields to required |
574 |
| - |
575 |
| -You can make all fields that don't have a default value be required by setting |
576 |
| -the `RequiredIfNoDef: true` in the `Options`. |
577 |
| - |
578 |
| -For example |
579 |
| - |
580 |
| -```go |
581 |
| -package main |
582 |
| - |
583 |
| -import ( |
584 |
| - "fmt" |
585 |
| - "log" |
586 |
| - |
587 |
| - "github.com/caarlos0/env/v11" |
588 |
| -) |
589 |
| - |
590 |
| -type Config struct { |
591 |
| - Username string `env:"USERNAME" envDefault:"admin"` |
592 |
| - Password string `env:"PASSWORD"` |
593 |
| -} |
594 |
| - |
595 |
| -func main() { |
596 |
| - cfg := &Config{} |
597 |
| - opts := env.Options{RequiredIfNoDef: true} |
598 |
| - |
599 |
| - // Load env vars. |
600 |
| - if err := env.ParseWithOptions(cfg, opts); err != nil { |
601 |
| - log.Fatal(err) |
602 |
| - } |
603 |
| - |
604 |
| - // Print the loaded data. |
605 |
| - fmt.Printf("%+v\n", cfg) |
606 |
| -} |
607 |
| -``` |
608 |
| - |
609 |
| -## Defaults from code |
610 |
| - |
611 |
| -You may define default value also in code, by initialising the config data |
612 |
| -before it's filled by `env.Parse`. |
613 |
| -Default values defined as struct tags will overwrite existing values during |
614 |
| -Parse. |
615 |
| - |
616 |
| -```go |
617 |
| -package main |
618 |
| - |
619 |
| -import ( |
620 |
| - "fmt" |
621 |
| - "log" |
622 |
| - |
623 |
| - "github.com/caarlos0/env/v11" |
624 |
| -) |
625 |
| - |
626 |
| -type Config struct { |
627 |
| - Username string `env:"USERNAME" envDefault:"admin"` |
628 |
| - Password string `env:"PASSWORD"` |
629 |
| -} |
630 |
| - |
631 |
| -func main() { |
632 |
| - cfg := Config{ |
633 |
| - Username: "test", |
634 |
| - Password: "123456", |
635 |
| - } |
636 |
| - |
637 |
| - if err := env.Parse(&cfg); err != nil { |
638 |
| - fmt.Println("failed:", err) |
639 |
| - } |
640 |
| - |
641 |
| - fmt.Printf("%+v", cfg) // {Username:admin Password:123456} |
642 |
| -} |
643 |
| -``` |
644 |
| - |
645 |
| -## Error handling |
646 |
| - |
647 |
| -You can handle the errors the library throws like so: |
648 |
| - |
649 |
| -```go |
650 |
| -package main |
651 |
| - |
652 |
| -import ( |
653 |
| - "fmt" |
654 |
| - "log" |
655 |
| - |
656 |
| - "github.com/caarlos0/env/v11" |
657 |
| -) |
658 |
| - |
659 |
| -type Config struct { |
660 |
| - Username string `env:"USERNAME" envDefault:"admin"` |
661 |
| - Password string `env:"PASSWORD"` |
662 |
| -} |
663 |
| - |
664 |
| -func main() { |
665 |
| - var cfg Config |
666 |
| - err := env.Parse(&cfg) |
667 |
| - if e, ok := err.(*env.AggregateError); ok { |
668 |
| - for _, er := range e.Errors { |
669 |
| - switch v := er.(type) { |
670 |
| - case env.ParseError: |
671 |
| - // handle it |
672 |
| - case env.NotStructPtrError: |
673 |
| - // handle it |
674 |
| - case env.NoParserError: |
675 |
| - // handle it |
676 |
| - case env.NoSupportedTagOptionError: |
677 |
| - // handle it |
678 |
| - default: |
679 |
| - fmt.Printf("Unknown error type %v", v) |
680 |
| - } |
681 |
| - } |
682 |
| - } |
683 |
| - |
684 |
| - fmt.Printf("%+v", cfg) // {Username:admin Password:123456} |
685 |
| -} |
686 |
| -``` |
687 |
| - |
688 |
| -> **Info** |
689 |
| -> |
690 |
| -> If you want to check if an specific error is in the chain, you can also use |
691 |
| -> `errors.Is()`. |
| 133 | +Examples are live in |
| 134 | +[pkg.go.dev](https://pkg.go.dev/github.com/caarlos0/env/v11), |
| 135 | +and also in the |
| 136 | +[example test file](./example_test.go). |
692 | 137 |
|
693 | 138 | ## Badges
|
694 | 139 |
|
695 | 140 | [](https://github.com/goreleaser/goreleaser/releases/latest)
|
696 | 141 | [](/LICENSE.md)
|
697 | 142 | [](https://github.com/caarlos0/env/actions?workflow=build)
|
698 | 143 | [](https://codecov.io/gh/caarlos0/env)
|
699 |
| -[](http://godoc.org/github.com/caarlos0/env/v11) |
| 144 | +[](http://godoc.org/github.com/caarlos0/env/v11) |
700 | 145 | [](https://github.com/goreleaser)
|
701 | 146 | [](https://conventionalcommits.org)
|
702 | 147 |
|
|
0 commit comments