Skip to content

Commit

Permalink
docs: update README (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmzane authored Nov 12, 2023
1 parent 9c591d6 commit 3acffef
Showing 1 changed file with 66 additions and 104 deletions.
170 changes: 66 additions & 104 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

![logo](logo.svg)

A lightweight package for loading environment variables into structs
🔍 Load environment variables into a config struct

[![awesome-go](https://awesome.re/badge.svg)](https://github.com/avelino/awesome-go#configuration)
[![checks](https://github.com/go-simpler/env/actions/workflows/checks.yml/badge.svg)](https://github.com/go-simpler/env/actions/workflows/checks.yml)
Expand All @@ -15,44 +15,42 @@ A lightweight package for loading environment variables into structs
## 📌 About

This package is made for apps that [store config in environment variables][1].
Its purpose is to replace multiple fragmented `os.Getenv` calls in `main.go`
with a single struct definition, which simplifies config management and improves
code readability.
Its purpose is to replace fragmented `os.Getenv` calls in `main.go` with a single struct definition,
which simplifies config management and improves code readability.

## 🚀 Features

* Support for all common types and user-defined types
* Options: [required](#required), [expand](#expand), [slice separator](#slice-separator)
* Configurable [source](#source) of environment variables
* Auto-generated [usage message](#usage-message)

## 📦 Install

```shell
go get go-simpler.org/env
```

## 🚀 Features

* Simple API
* Dependency-free
* Per-variable options: [required](#required), [expand](#expand)
* Global options: [source](#source), [slice separator](#slice-separator)
* Auto-generated [usage message](#usage-message)

## 📋 Usage

`Load` is the main function of this package. It loads environment variables into
the provided struct.
`Load` is the main function of the package.
It loads environment variables into the given struct.

The struct fields must have the `env:"VAR"` struct tag, where `VAR` is the name
of the corresponding environment variable. Unexported fields are ignored.
The struct fields must have the `env:"VAR"` struct tag,
where VAR is the name of the corresponding environment variable.
Unexported fields are ignored.

```go
os.Setenv("PORT", "8080")

var cfg struct {
Port int `env:"PORT"`
}
if err := env.Load(&cfg); err != nil {
if err := env.Load(&cfg, nil); err != nil {
fmt.Println(err)
}

fmt.Println(cfg.Port)
// Output: 8080
fmt.Println(cfg.Port) // 8080
```

### Supported types
Expand All @@ -64,78 +62,50 @@ fmt.Println(cfg.Port)
* `time.Duration`
* `encoding.TextUnmarshaler`
* slices of any type above
* nested structs of any depth

See the `strconv.Parse*` functions for parsing rules.

Nested structs of any depth level are supported, only the leaves of the config
tree must have the `env` tag.

```go
os.Setenv("HTTP_PORT", "8080")

var cfg struct {
HTTP struct {
Port int `env:"HTTP_PORT"`
}
}
if err := env.Load(&cfg); err != nil {
fmt.Println(err)
}

fmt.Println(cfg.HTTP.Port)
// Output: 8080
```
See the `strconv.Parse*` functions for the parsing rules.
User-defined types can be used by implementing the `encoding.TextUnmarshaler` interface.

### Default values

Default values can be specified using the `default` struct tag:
Default values can be specified using the `default:"VALUE"` struct tag:

```go
os.Unsetenv("PORT")

var cfg struct {
Port int `env:"PORT" default:"8080"`
}
if err := env.Load(&cfg); err != nil {
if err := env.Load(&cfg, nil); err != nil {
fmt.Println(err)
}

fmt.Println(cfg.Port)
// Output: 8080
fmt.Println(cfg.Port) // 8080
```

### Per-variable options

The name of the environment variable can be followed by comma-separated options
in the form of `env:"VAR,option1,option2,..."`.

#### Required
### Required

Use the `required` option to mark the environment variable as required. In case
no such variable is found, an error of type `NotSetError` will be returned.
Use the `required` option to mark an environment variable as required.
If it is not set, an error of type `NotSetError` is returned.

```go
os.Unsetenv("HOST")
os.Unsetenv("PORT")

var cfg struct {
Host string `env:"HOST,required"`
Port int `env:"PORT,required"`
Port int `env:"PORT,required"`
}
if err := env.Load(&cfg); err != nil {
if err := env.Load(&cfg, nil); err != nil {
var notSetErr *env.NotSetError
if errors.As(err, &notSetErr) {
fmt.Println(notSetErr.Names)
fmt.Println(notSetErr) // env: PORT is required but not set
}
}

// Output: [HOST PORT]
```

#### Expand
### Expand

Use the `expand` option to automatically expand the value of the environment
variable using `os.Expand`.
Use the `expand` option to automatically expand the value of an environment variable using `os.Expand`.

```go
os.Setenv("PORT", "8080")
Expand All @@ -144,91 +114,83 @@ os.Setenv("ADDR", "localhost:${PORT}")
var cfg struct {
Addr string `env:"ADDR,expand"`
}
if err := env.Load(&cfg); err != nil {
if err := env.Load(&cfg, nil); err != nil {
fmt.Println(err)
}

fmt.Println(cfg.Addr)
// Output: localhost:8080
fmt.Println(cfg.Addr) // localhost:8080
```

### Global options

`Load` also accepts global options that apply to all environment variables.
### Slice separator

#### Source

By default, `Load` retrieves environment variables values directly from OS.
To use a different source, provide an implementation of the `Source` interface via the `WithSource` option.

```go
// Source represents a source of environment variables.
type Source interface {
// LookupEnv retrieves the value of the environment variable named by the key.
LookupEnv(key string) (value string, ok bool)
}
```

Here's an example of using `Map`, a builtin `Source` implementation useful in tests:
Space is the default separator used to parse slice values.
It can be changed with `Options.SliceSep`:

```go
m := env.Map{"PORT": "8080"}
os.Setenv("PORTS", "8080,8081,8082")

var cfg struct {
Port int `env:"PORT"`
Ports []int `env:"PORTS"`
}
if err := env.Load(&cfg, env.WithSource(m)); err != nil {
if err := env.Load(&cfg, &env.Options{SliceSep: ","}); err != nil {
fmt.Println(err)
}

fmt.Println(cfg.Port)
// Output: 8080
fmt.Println(cfg.Ports) // [8080 8081 8082]
```

#### Slice separator
### Source

Space is the default separator when parsing slice values. It can be changed
using the `WithSliceSeparator` option:
By default, `Load` retrieves environment variables directly from OS.
To use a different source, pass an implementation of the `Source` interface via `Options.Source`.

```go
os.Setenv("PORTS", "8080;8081;8082")
type Source interface {
LookupEnv(key string) (value string, ok bool)
}
```

Here's an example of using `Map`, a `Source` implementation useful in tests:

```go
m := env.Map{"PORT": "8080"}

var cfg struct {
Ports []int `env:"PORTS"`
Port int `env:"PORT"`
}
if err := env.Load(&cfg, env.WithSliceSeparator(";")); err != nil {
if err := env.Load(&cfg, &env.Options{Source: m}); err != nil {
fmt.Println(err)
}

fmt.Println(cfg.Ports)
// Output: [8080 8081 8082]
fmt.Println(cfg.Port) // 8080
```

### Usage message

`env` supports printing an auto-generated usage message the same way the `flag` package does it.
The `Usage` function prints a usage message documenting all defined environment variables.

```go
os.Unsetenv("DB_HOST")
os.Unsetenv("DB_PORT")

var cfg struct {
DB struct {
Host string `env:"DB_HOST,required" desc:"database host"`
Port int `env:"DB_PORT,required" desc:"database port"`
Host string `env:"DB_HOST,required" usage:"database host"`
Port int `env:"DB_PORT,required" usage:"database port"`
}
HTTPPort int `env:"HTTP_PORT" default:"8080" desc:"http server port"`
HTTPPort int `env:"HTTP_PORT" default:"8080" usage:"http server port"`
}
if err := env.Load(&cfg); err != nil {
if err := env.Load(&cfg, nil); err != nil {
fmt.Println(err)
env.Usage(&cfg, os.Stdout)
}
```

// Output: env: [DB_HOST DB_PORT] are required but not set
// Usage:
// DB_HOST string required database host
// DB_PORT int required database port
// HTTP_PORT int default 8080 http server port
```
Usage:
DB_HOST string required database host
DB_PORT int required database port
HTTP_PORT int default 8080 http server port
```

[1]: https://12factor.net/config

0 comments on commit 3acffef

Please sign in to comment.