# `Nvir`
[🔗](https://github.com/lud/nvir/blob/v0.16.3/lib/nvir.ex#L1)

This is the main API for Nvir, an environment variable loader and validator.

The most useful documentation is generally:

* The [README](README.md#basic-usage) for usage instructions.
* The `dotenv!/1` function.

# `config_opt`

```elixir
@type config_opt() ::
  {:enabled_sources, %{required(atom()) =&gt; boolean()}}
  | {:parser, module()}
  | {:cd, nil | Path.t()}
  | {:before_env_set, pair_transformer()}
  | {:before_env_set_all,
     (%{required(binary()) =&gt; binary()} -&gt; Enumerable.t(var_def()))
     | {module(), atom(), [term()]}}
```

# `pair_transformer`

```elixir
@type pair_transformer() :: (var_def() -&gt; var_def()) | {module(), atom(), [term()]}
```

# `source`

```elixir
@type source() :: binary() | {atom(), source()} | [source()]
```

# `sources`

```elixir
@type sources() :: source() | [sources()] | {atom(), sources()}
```

# `t`

```elixir
@type t() :: %Nvir{
  before_env_set: term(),
  before_env_set_all: term(),
  cd: nil | Path.t(),
  enabled_sources: %{required(atom()) =&gt; boolean()},
  parser: module()
}
```

# `var_def`

```elixir
@type var_def() :: {String.t(), String.t()}
```

# `default_dotenv_sources`

Returns the sources enabled by default when using `dotenv/1` or
`dotenv_loader/0`. The value changes dynamically depending on the current
environment and operating system.

See the "Predefined tags" section on the `dotenv!/1` documentation.

# `dotenv!`

```elixir
@spec dotenv!(sources()) :: %{required(binary()) =&gt; binary()}
```

Loads specified dotenv files in the system environment. Intended usage is from
`config/runtime.exs` in your project

Variables defined in the files will not overwrite the system environment if
they are already defined. To overwrite the system env, please list your files
under an `:overwrite` key.

This function takes multiple sources and will select the sources to actually
load based on system properties.

Valid sources are:

* A string, this is an actual file that we want to load.
* A `{tag, value}` tuple where the tag is an atom and the value is a source.
  Predefined tags are listed below. Additional tags can be defined with
  `enable_sources/3`.
* A list of sources. So a keyword list is a valid source, _i.e._ a list of
  tagged tuples.

Files are loaded in order of appearance, in two phases:

* First, files that are not wrapped in an `:overwrite` tagged tuple.
* Then files that are wrapped in such tuples.

Files that do not exist are safely ignored.

### Examples

    import Config
    import Nvir

    # Load a single file
    dotenv!(".env")

    # Load multiple files
    dotenv!([".env", ".env.#{config_env()}"])

    # Load files depending on environment
    dotenv!(
      dev: ".env.dev",
      test: ".env.test"
    )

    # Load files with and without overwrite
    dotenv!(
      dev: ".env",
      test: [".env", ".env.test"],
      overwrite: [test: ".env.test.local"]
    )

    # Overwrite the system with all existing files
    dotenv!(
      overwrite: [
        dev: ".env",
        test: [".env", ".env.test", ".env.test.local"]
      ]
    )

    # Totally useless but valid :)
    dotenv!(test: [test: [test: ".env.test"]])
    # Same without wrapping the tuples in lists
    dotenv!({:test, {:test, {:test, ".env.test"}}})

    # This will not load the file as `:test` and `:dev` will not be
    # enabled at the same time
    dotenv!(dev: [test: ".env.test"])

### Predefined tags

Tags are enabled under different circumstances.

#### Mix environment

* `:dev` - When `Config.config_env()` or `Mix.env()` is `:dev`.
* `:test` - When `Config.config_env()` or `Mix.env()` is `:test`.

There is no predefined tag for `:prod` as using dotenv files in production is
strongly discouraged.

#### Continuous integration

* `:ci` - When the `CI` environment variable is `"true"`.
* `:ci@github` - When the `GITHUB_ACTIONS` environment variable is `"true"`.
* `:ci@travis` - When the `TRAVIS` environment variable is `"true"`.
* `:ci@circle` - When the `CIRCLECI` environment variable is `"true"`.
* `:ci@gitlab` - When the `GITLAB_CI` environment variable is `"true"`.

#### Operating system

* `:linux` - On Linux machines.
* `:windows` - On Windows machines.
* `:darwin` - On MacOS machines.

# `dotenv!`

```elixir
@spec dotenv!(t(), sources()) :: %{required(binary()) =&gt; binary()}
```

Same as `dotenv!/1` but accepts a custom configuration to load dotenv files.

# `dotenv_configure`

```elixir
@spec dotenv_configure(t(), [config_opt()]) :: t()
```

Updates the given configuration with the given options.

The options are not merged.

### Options

* `:enabled_sources` - A map of `%{atom => boolean}` values to declare which
  source tags will be enabled when collecting sources. Defaults to the return
  value of `default_enabled_sources/0`.
* `:parser` - The module to parse environment variables files. Defaults to
  `m:Nvir.Parser.DefaultParser`.
* `:cd` - A directory path to load relative source paths from.
* `:before_env_set` - A function or `{module, function, args}` tuple that
  accepts a `{varname, value}` pair and must return a similar tuple. This
  gives the possibility to change or transform the parsed variables before the
  environment is altered. Returned `varname` and `value` must implement the
  `String.Chars` protocol. Returning `nil` as a value will delete the
  environment variable. When an MFA tuple is given, the pair is passed as the
  first argument.
* `:before_env_set_all` - A function or `{module, function, args}` tuple that
  accepts an map of `%{varname => value}` and must return an enumerable of
  `{varname, value}` to set. This gives the possibility to skip some
  variables, or to change their values before the environment is altered.
  Returned `varname` and `value` must implement the `String.Chars` protocol.
  Returning `nil` as a value will delete the environment variable. When an MFA
  tuple is given, the pair is passed as the first argument. This hook is
  called after `:before_env_set` so if you define both hooks, it will receive
  the values altered by `:before_env_set`.

### Example

Implement loading a custom `:docs` environment and load a file when running a
release:

    import Nvir

    dotenv_loader()
    |> dotenv_configure(
      enabled_sources: %{
        # Enable sources tagged with :docs depending on an environment variable
        docs: System.get_env("MIX_ENV") == "docs",

        # Enable sources tagged with :rel when running a release
        rel: env!("RELEASE_NAME", :boolean, false)
      },

      # Load dotenv files relative to this directory
      cd: "~/projects/apps/envs"
    )
    |> dotenv!(
      docs: ".env.docs",
      rel: "releases.env",
      dev: ".env.dev",
      test: ".env.test"
    )

# `dotenv_enable_sources`

Like `dotenv_enable_sources/3` but accepts a keyword list or map of sources.

    Nvir.dotenv_loader()
    |> Nvir.dotenv_enable_source(
      custom: true,
      docs: config_env() == :docs
    )
    |> Nvir.dotenv!(["global.env", custom: "local.env", docs: "docs.env"])

# `dotenv_enable_sources`

Enables or disables environment variable sources under the given tag.

For instance, the following call will load both files:

    Nvir.dotenv_loader()
    |> Nvir.enable_sources(:custom, true)
    |> Nvir.dotenv!(["global.env", custom: "local.env"])

Whereas the following call will only load files that are not wrapped in a tag.

    Nvir.dotenv_loader()
    |> Nvir.dotenv!(["global.env", custom: "local.env"])

It is also possible to disable some defaults by overriding them. In the
following code, the `.env.test` file will never be loaded:

    Nvir.dotenv_loader()
    |> Nvir.enable_sources(:test, false)
    |> Nvir.dotenv!(["global.env", dev: ".env.dev", test: ".env.test"])

# `dotenv_loader`

```elixir
@spec dotenv_loader() :: t()
```

Returns the default configuration for the `dotenv!/2` function.

### Examples

Implement loading a custom `:docs` environment:

    import Config
    import Nvir

    dotenv_loader()
    |> dotenv_enable_source(:docs, config_env() == :docs)
    |> dotenv!(
      docs: ".env.docs",
      dev: ".env.dev",
      test: ".env.test"
    )

# `dotenv_new`

```elixir
@spec dotenv_new() :: t()
```

Returns a configuration for `dotenv!/2` without any enabled source.

Generally this is used for custom loading strategies, see `dotenv_loader/0` to
use reasonable defaults.

# `env!`

Returns the value of the given `var`, transformed and validated by the given
`caster`.

Raises if the variable is not defined or if the caster validation fails.

Please see the [README](README.md#available-casters) for available casters.

# `env!`

Returns the value of the given `var`, transformed and validated by the given
`caster`.

Returns the `default` value if the variable is not defined.

If `default` is a function, it is called only if the variable is not defined.
The returned value from `env!/3` will be the return value of the function.

Raises if the caster validation fails.

Please see the [README](README.md#available-casters) for available casters.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
