Skip to content

Commit

Permalink
docs: improving docs and examples
Browse files Browse the repository at this point in the history
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
  • Loading branch information
caarlos0 committed May 23, 2024
1 parent c4db909 commit 587c89a
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 189 deletions.
112 changes: 13 additions & 99 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@

[![Build Status](https://img.shields.io/github/actions/workflow/status/caarlos0/env/build.yml?branch=main&style=for-the-badge)](https://github.com/caarlos0/env/actions?workflow=build)
[![Coverage Status](https://img.shields.io/codecov/c/gh/caarlos0/env.svg?logo=codecov&style=for-the-badge)](https://codecov.io/gh/caarlos0/env)
[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://pkg.go.dev/github.com/caarlos0/env/v11)
[![Go Docs](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://pkg.go.dev/github.com/caarlos0/env/v11)

A simple and zero-dependencies library to parse environment variables into
`struct`s.
A simple and zero-dependencies library to parse environment variables into `struct`s.

```go
type config struct {
Home string `env:"HOME"`
}

cfg, err := env.ParseAs[config]()
```

You can see the full list of examples
[here](https://pkg.go.dev/github.com/caarlos0/env/v11#pkg-examples).

## Used and supported by

Expand All @@ -19,60 +29,6 @@ A simple and zero-dependencies library to parse environment variables into
<br/>
</p>

## Example

Get the module with:

```sh
go get github.com/caarlos0/env/v11
```

The usage looks like this:

```go
package main

import (
"fmt"
"time"

"github.com/caarlos0/env/v11"
)

type config struct {
Home string `env:"HOME"`
Port int `env:"PORT" envDefault:"3000"`
Password string `env:"PASSWORD,unset"`
IsProduction bool `env:"PRODUCTION"`
Duration time.Duration `env:"DURATION"`
Hosts []string `env:"HOSTS" envSeparator:":"`
TempFolder string `env:"TEMP_FOLDER,expand" envDefault:"${HOME}/tmp"`
StringInts map[string]int `env:"MAP_STRING_INT"`
}

func main() {
cfg := config{}
if err := env.Parse(&cfg); err != nil {
fmt.Printf("%+v\n", err)
}

// or you can use generics
cfg, err := env.ParseAs[config]()
if err != nil {
fmt.Printf("%+v\n", err)
}

fmt.Printf("%+v\n", cfg)
}
```

You can run it like this:

```sh
$ PRODUCTION=true HOSTS="host1:host2:host3" DURATION=1s MAP_STRING_INT=k1:1,k2:2 go run main.go
{Home:/your/home Port:3000 IsProduction:true Hosts:[host1 host2 host3] Duration:1s StringInts:map[k1:1 k2:2]}
```

## Caveats

> [!CAUTION]
Expand Down Expand Up @@ -185,48 +141,6 @@ type config struct {
> value.
> This also means that custom parser funcs will not be invoked.
## Expand vars

If you set the `expand` option, environment variables (either in `${var}` or
`$var` format) in the string will be replaced according with the actual value
of the variable. For example:

```go
type config struct {
SecretKey string `env:"SECRET_KEY,expand"`
}
```

This also works with `envDefault`:

```go
import (
"fmt"
"github.com/caarlos0/env/v11"
)

type config struct {
Host string `env:"HOST" envDefault:"localhost"`
Port int `env:"PORT" envDefault:"3000"`
Address string `env:"ADDRESS,expand" envDefault:"$HOST:${PORT}"`
}

func main() {
cfg := config{}
if err := env.Parse(&cfg); err != nil {
fmt.Printf("%+v\n", err)
}
fmt.Printf("%+v\n", cfg)
}
```

results in this:

```sh
$ PORT=8080 go run main.go
{Host:localhost Port:8080 Address:localhost:8080}
```

## Not Empty fields

While `required` demands the environment variable to be set, it doesn't check
Expand Down
27 changes: 27 additions & 0 deletions env.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
// Package env parses environment variables into structs.
//
// type config struct {
// Home string `env:"HOME"`
// }
// cfg, err := env.ParseAs[config]()
//
// The following types are supported by default:
// - string
// - bool
// - int
// - int8
// - int16
// - int32
// - int64
// - uint
// - uint8
// - uint16
// - uint32
// - uint64
// - float32
// - float64
// - time.Duration
// - encoding.TextUnmarshaler
// - url.URL
//
// You can, however, implement custom parsers by setting the FuncMap option.
package env

import (
Expand Down
90 changes: 0 additions & 90 deletions env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1353,70 +1353,6 @@ func TestParseInvalidURL(t *testing.T) {
isTrue(t, errors.Is(err, ParseError{}))
}

func ExampleParse() {
type inner struct {
Foo string `env:"FOO" envDefault:"foobar"`
}
type config struct {
Home string `env:"HOME,required"`
Port int `env:"PORT" envDefault:"3000"`
IsProduction bool `env:"PRODUCTION"`
TempFolder string `env:"TEMP_FOLDER,expand" envDefault:"${HOME}/.tmp"`
StringInts map[string]int `env:"MAP_STRING_INT" envDefault:"k1:1,k2:2"`
Inner inner
}
os.Setenv("HOME", "/tmp/fakehome")
var cfg config
if err := Parse(&cfg); err != nil {
fmt.Println("failed:", err)
}
fmt.Printf("%+v", cfg)
// Output: {Home:/tmp/fakehome Port:3000 IsProduction:false TempFolder:/tmp/fakehome/.tmp StringInts:map[k1:1 k2:2] Inner:{Foo:foobar}}
}

func ExampleParse_onSet() {
type config struct {
Home string `env:"HOME,required"`
Port int `env:"PORT" envDefault:"3000"`
IsProduction bool `env:"PRODUCTION"`
NoEnvTag bool
Inner struct{} `envPrefix:"INNER_"`
}
os.Setenv("HOME", "/tmp/fakehome")
var cfg config
if err := ParseWithOptions(&cfg, Options{
OnSet: func(tag string, value interface{}, isDefault bool) {
fmt.Printf("Set %s to %v (default? %v)\n", tag, value, isDefault)
},
}); err != nil {
fmt.Println("failed:", err)
}
fmt.Printf("%+v", cfg)
// Output: Set HOME to /tmp/fakehome (default? false)
// Set PORT to 3000 (default? true)
// Set PRODUCTION to (default? false)
// {Home:/tmp/fakehome Port:3000 IsProduction:false NoEnvTag:false Inner:{}}
}

func ExampleParse_defaults() {
type config struct {
A string `env:"FOO" envDefault:"foo"`
B string `env:"FOO"`
}

// env FOO is not set

cfg := config{
A: "A",
B: "B",
}
if err := Parse(&cfg); err != nil {
fmt.Println("failed:", err)
}
fmt.Printf("%+v", cfg)
// Output: {A:foo B:B}
}

func TestIgnoresUnexported(t *testing.T) {
type unexportedConfig struct {
home string `env:"HOME"`
Expand Down Expand Up @@ -1466,32 +1402,6 @@ func TestPrecedenceUnmarshalText(t *testing.T) {
isEqual(t, []LogLevel{DebugLevel, InfoLevel}, cfg.LogLevels)
}

func ExampleParseWithOptions() {
type thing struct {
desc string
}

type conf struct {
Thing thing `env:"THING"`
}

os.Setenv("THING", "my thing")

c := conf{}

err := ParseWithOptions(&c, Options{FuncMap: map[reflect.Type]ParserFunc{
reflect.TypeOf(thing{}): func(v string) (interface{}, error) {
return thing{desc: v}, nil
},
}})
if err != nil {
fmt.Println(err)
}
fmt.Println(c.Thing.desc)
// Output:
// my thing
}

func TestFile(t *testing.T) {
type config struct {
SecretKey string `env:"SECRET_KEY,file"`
Expand Down
Loading

0 comments on commit 587c89a

Please sign in to comment.