# `Enviable`
[🔗](https://github.com/halostatue/enviable/blob/v2.3.0/lib/enviable.ex#L1)

Enviable is a small collection of functions to improve Elixir project configuration via
environment variables as proposed under the [12-factor][12f] application model. It works
well with configuration environment loaders like [Dotenvy][Dotenvy] or [Nvir][nvir] and
provides robust value conversion like [jetenv][jetenv].

### Usage

Enviable will typically be imported in `config/runtime.exs` after `Config`, but may be
used anywhere that environment variables are read.

```elixir
# config/runtime.exs
import Config
import Enviable

client = fetch_env!("CLIENT")
Dotenvy.source([".env", ".env.#{client}", get_env()], side_effect: &put_env/1)

# Before
#
# config :my_app,
#   key: System.fetch_env!("SECRET_KEY"),
#   port: System.fetch_env!("PORT") |> String.to_integer(),
#   ssl: System.get_env("SSL_ENABLED") in ~w[1 true]

# After
config :my_app,
  key: fetch_env!("SECRET_KEY"),
  port: fetch_env_as_integer!("PORT"),
  ssl: get_env_as_boolean("SSL_ENABLED")
```

> #### Info {: .info}
>
> When using Dotenvy, the use of a `side_effect` that calls `System.put_env/1` is
> **required**, as Enviable works with the system environment variable table. Future
> versions of Enviable may offer ways to work with the default Dotenvy behaviour.

### Configuration

Envible has two compile-time options.

- `:boolean_downcase`: Sets the default value for case-folding boolean conversions. Can
  be set to `:default`, `:ascii`, `:greek`, or `:turkic`. The boolean value `true` will
  be treated as `:default`.

  ```elixir
  config :enviable, :boolean_downcase, true
  config :enviable, :boolean_downcase, :default
  config :enviable, :boolean_downcase, :ascii
  ```

  If unspecified, the value is `:default`.

- `:json_engine`: The default JSON engine to use for JSON conversions. This may be
  provided as a `t:module/0` (which must export `decode/1`) or a `t:mfa/0` tuple. When
  provided with a `t:mfa/0`, the variable value will be passed as the first parameter.

  If the engine produces `{:ok, json_value}` or an expected JSON type result, it will be
  considered successful. Any other result will be treated as failure.

  The default JSON engine is `JSON` if Elixir's `m:JSON` is available, `:json` if the
  Erlang/OTP `m::json` module is available (Erlang/OTP 27+) or provided by
  [json_polyfill][jp]. Otherwise, [Jason][jason] is the default engine.

  ```elixir
  config :enviable, :json_engine, :thoas
  config :enviable, :json_engine, {Jason, :decode, [[floats: :decimals]]}
  ```

[12f]: https://12factor.net/
[dotenvy]: https://hexdocs.pm/dotenvy/readme.html
[jason]: https://hexdocs.pm/jason/readme.html
[jetenv]: https://hexdocs.pm/jetenv/readme.html
[jp]: https://hexdocs.pm/json_polyfill/readme.html
[nvir]: https://hexdocs.pm/nvir/readme.html

# `as_list_opt`

```elixir
@type as_list_opt() ::
  {:as, :string | Enviable.Conversion.primitive()}
  | {:delimiter, String.t(), [String.t()], Regex.t(), :binary.cp()}
  | {:parts, pos_integer() | :infinity}
  | {:trim, boolean()}
  | {:on,
     :all | :first | :all_but_first | :none | :all_names | [binary() | atom()]}
  | {:include_captures, boolean()}
  | {atom(), term()}
```

# `put_env_new`

```elixir
@spec put_env_new(String.t(), String.t()) :: :ok
```

Set an environment variable value only if it is not yet set. This is a convenience
wrapper around `System.put_env/2` and `System.get_env/2`.

### Examples

```elixir
iex> Enviable.put_env_new("PORT", "3000")
:ok
iex> Enviable.get_env("PORT")
"3000"
iex> Enviable.put_env_new("PORT", "5000")
:ok
iex> Enviable.get_env("PORT")
"3000"
```

# `fetch_env_as`
*since 1.1.0* 

```elixir
@spec fetch_env_as(String.t(), Enviable.Conversion.conversion(), keyword()) ::
  {:ok, term()} | :error
```

Returns the value of an environment variable converted to the target `type` as
`{:ok, term()}` or `:error` if the variable is unset.

Supported primitive conversions are:

- `:atom` (`t:Enviable.Conversion.convert_atom/0`, `fetch_env_as_atom/2`)

- `:boolean` (`t:Enviable.Conversion.convert_boolean/0`, `fetch_env_as_boolean/2`)

- `:charlist` (`t:Enviable.Conversion.convert_charlist/0`, `fetch_env_as_charlist/1`)

- `:decimal` (`t:Enviable.Conversion.convert_decimal/0`, `fetch_env_as_decimal/1`)

- `:duration` (`t:Enviable.Conversion.convert_duration/0`, `fetch_env_as_duration/2`)

- `:elixir` (`t:Enviable.Conversion.convert_elixir/0`, `fetch_env_as_elixir/1`)

- `:erlang` (`t:Enviable.Conversion.convert_erlang/0`, `fetch_env_as_erlang/1`)

- `:float` (`t:Enviable.Conversion.convert_float/0`, `fetch_env_as_float/1`)

- `:integer` (`t:Enviable.Conversion.convert_integer/0`, `fetch_env_as_integer/2`)

- `:json` (`t:Enviable.Conversion.convert_json/0`, `fetch_env_as_json/2`)

- `:log_level` (`t:Enviable.Conversion.convert_log_level/0`, `fetch_env_as_log_level/1`)

- `:module` (`t:Enviable.Conversion.convert_module/0`, `fetch_env_as_module/2`)

- `:pem` (`t:Enviable.Conversion.convert_pem/0`, `fetch_env_as_pem/2`)

- `:safe_atom` (`t:Enviable.Conversion.convert_safe_atom/0`, `fetch_env_as_safe_atom/2`)

- `:safe_module` (`t:Enviable.Conversion.convert_safe_module/0`,
  `fetch_env_as_safe_module/2`)

- `:timeout` (`t:Enviable.Conversion.convert_timeout/0`, `fetch_env_as_timeout/1`)

Supported encoded conversions are:

- `:base16` (`t:Enviable.Conversion.encoded_base16/0`, `fetch_env_as_base16/2`)

- `:base32` (`t:Enviable.Conversion.encoded_base32/0`, `fetch_env_as_base32/2`)

- `:base64`, `:url_base64` (`t:Enviable.Conversion.encoded_base64/0`,
  `fetch_env_as_base64/2`, `fetch_env_as_url_base64/2`)
- `:hex32` (`t:Enviable.Conversion.encoded_hex32/0`, `fetch_env_as_hex32/2`)

- `:list` (`t:Enviable.Conversion.encoded_list/0`, `fetch_env_as_list/2`)

See `Enviable.Conversion` for supported type conversions and options, but note that any
`default` values are ignored for `fetch_env_as/3` and related `fetch_env_as_*`
functions.

### Examples

```elixir
iex> Enviable.fetch_env_as("UNSET", :atom)
:error

iex> Enviable.fetch_env_as("UNSET", :float)
:error

iex> Enviable.fetch_env_as("UNSET", :base16)
:error

iex> Enviable.put_env("NAME", "fetch_env_as")
iex> Enviable.fetch_env_as("NAME", :atom)
{:ok, :fetch_env_as}

iex> Enviable.put_env("NAME", "FETCH_ENV_AS")
iex> Enviable.fetch_env_as("NAME", :safe_atom, downcase: true)
{:ok, :fetch_env_as}

iex> Enviable.fetch_env_as("UNSET", :float, default: "3.5")
:error

iex> Enviable.fetch_env_as("UNSET", :float, default: 3.5)
:error

iex> Enviable.put_env("FLOAT", "3")
iex> Enviable.fetch_env_as("FLOAT", :float)
{:ok, 3.0}

iex> Enviable.put_env("FLOAT", "3.1")
iex> Enviable.fetch_env_as("FLOAT", :float)
{:ok, 3.1}

iex> red = Base.encode16("RED", case: :lower)
iex> Enviable.put_env("NAME", red)
iex> Enviable.fetch_env_as("NAME", :base16, case: :lower)
{:ok, "RED"}
iex> Enviable.fetch_env_as("NAME", {:base16, :string}, case: :lower)
{:ok, "RED"}
iex> Enviable.fetch_env_as("NAME", {:base16, :atom}, case: :lower, downcase: true)
{:ok, :red}
```

# `fetch_env_as!`
*since 1.1.0* 

```elixir
@spec fetch_env_as!(String.t(), Enviable.Conversion.conversion(), keyword()) :: term()
```

Returns the value of an environment variable converted to the target `type` or raises an
exception if the variable is unset.

Supported primitive conversions are:

- `:atom` (`t:Enviable.Conversion.convert_atom/0`, `fetch_env_as_atom!/2`)

- `:boolean` (`t:Enviable.Conversion.convert_boolean/0`, `fetch_env_as_boolean!/2`)

- `:charlist` (`t:Enviable.Conversion.convert_charlist/0`, `fetch_env_as_charlist!/2`)

- `:decimal` (`t:Enviable.Conversion.convert_decimal/0`, `fetch_env_as_decimal!/2`)

- `:duration` (`t:Enviable.Conversion.convert_duration/0`, `fetch_env_as_duration!/2`)

- `:elixir` (`t:Enviable.Conversion.convert_elixir/0`, `fetch_env_as_elixir!/1`)

- `:erlang` (`t:Enviable.Conversion.convert_erlang/0`, `fetch_env_as_erlang!/1`)

- `:float` (`t:Enviable.Conversion.convert_float/0`, `fetch_env_as_float!/2`)

- `:integer` (`t:Enviable.Conversion.convert_integer/0`, `fetch_env_as_integer!/2`)

- `:json` (`t:Enviable.Conversion.convert_json/0`, `fetch_env_as_json!/2`)

- `:log_level` (`t:Enviable.Conversion.convert_log_level/0`,
  `fetch_env_as_log_level!/2`)

- `:module` (`t:Enviable.Conversion.convert_module/0`, `fetch_env_as_module!/2`)

- `:pem` (`t:Enviable.Conversion.convert_pem/0`, `fetch_env_as_pem!/2`)

- `:safe_atom` (`t:Enviable.Conversion.convert_safe_atom/0`,
  `fetch_env_as_safe_atom!/2`)

- `:safe_module` (`t:Enviable.Conversion.convert_safe_module/0`,
  `fetch_env_as_safe_module!/2`)

- `:timeout` (`t:Enviable.Conversion.convert_timeout/0`, `fetch_env_as_timeout!/2`)

Supported encoded conversions are:

- `:base16` (`t:Enviable.Conversion.encoded_base16/0`, `fetch_env_as_base16!/2`)

- `:base32` (`t:Enviable.Conversion.encoded_base32/0`, `fetch_env_as_base32!/2`)

- `:base64`, `:url_base64` (`t:Enviable.Conversion.encoded_base64/0`,
  `fetch_env_as_base64!/2`, `fetch_env_as_url_base64!/2`)
- `:hex32` (`t:Enviable.Conversion.encoded_hex32/0`, `fetch_env_as_hex32!/2`)

- `:list` (`t:Enviable.Conversion.encoded_list/0`, `fetch_env_as_list!/2`)

See `Enviable.Conversion` for supported type conversions and options, but note that any
documented `default` values are ignored for `fetch_env_as!/3` and related
`fetch_env_as_*!` functions.

### Examples

```elixir
iex> Enviable.fetch_env_as!("UNSET", :atom)
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.fetch_env_as!("UNSET", :float)
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.fetch_env_as!("UNSET", :base16)
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("NAME", "fetch_env_as!")
iex> Enviable.fetch_env_as!("NAME", :atom)
:fetch_env_as!

iex> Enviable.put_env("NAME", "FETCH_ENV_AS!")
iex> Enviable.fetch_env_as!("NAME", :safe_atom, downcase: true)
:fetch_env_as!

iex> Enviable.fetch_env_as!("UNSET", :float, default: "3.5")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.fetch_env_as!("UNSET", :float, default: 3.5)
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("FLOAT", "3")
iex> Enviable.fetch_env_as!("FLOAT", :float)
3.0

iex> Enviable.put_env("FLOAT", "3.1")
iex> Enviable.fetch_env_as!("FLOAT", :float)
3.1

iex> red = Base.encode16("RED", case: :lower)
iex> Enviable.put_env("NAME", red)
iex> Enviable.fetch_env_as!("NAME", :base16, case: :lower)
"RED"
iex> Enviable.fetch_env_as!("NAME", {:base16, :string}, case: :lower)
"RED"
iex> Enviable.fetch_env_as!("NAME", {:base16, :atom}, case: :lower, downcase: true)
:red
```

# `fetch_env_as_atom`
*since 1.3.0* 

```elixir
@spec fetch_env_as_atom(String.t(), [
  {:allowed, [atom()]} | Enviable.Conversion.opt_atom_downcase()
]) ::
  {:ok, atom()} | :error
```

Returns the value of an environment variable converted to `t:atom/0` as `{:ok, atom()}`
or `:error` if the variable is unset.

> #### Untrusted Input {: .warning}
>
> This conversion routine uses `String.to_atom/1` and may result in atom exhaustion if
> used without the `:allowed` option. See [Preventing atom exhaustion](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/atom_exhaustion) from the Security Working
> Group of the Erlang Ecosystem Foundation.

### Options

- `:allowed`: A list of `t:atom/0` values indicating permitted atoms and used as
  a lookup table, if present. Any value not found will result in an exception.

- `:downcase`: See `t:Enviable.Conversion.opt_atom_downcase/0`.

- `:upcase`: See `t:Enviable.Conversion.opt_atom_upcase/0`.

### Examples

```elixir
iex> Enviable.fetch_env_as_atom("UNSET")
:error

iex> Enviable.put_env("NAME", "fetch_env_as_atom")
iex> Enviable.fetch_env_as_atom("NAME")
{:ok,  :fetch_env_as_atom}
```

# `fetch_env_as_atom!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_atom!(String.t(), [
  {:allowed, [atom()]} | Enviable.Conversion.opt_atom_downcase()
]) ::
  atom()
```

Returns the value of an environment variable converted to `t:atom/0` or raises an
exception if the variable is unset.

> #### Untrusted Input {: .warning}
>
> This conversion routine uses `String.to_atom/1` and may result in atom exhaustion if
> used without the `:allowed` option. See [Preventing atom exhaustion](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/atom_exhaustion) from the Security Working
> Group of the Erlang Ecosystem Foundation.

### Options

- `:allowed`: A list of `t:atom/0` values indicating permitted atoms and used as
  a lookup table, if present. Any value not found will result in an exception.

- `:downcase`: See `t:Enviable.Conversion.opt_atom_downcase/0`.

- `:upcase`: See `t:Enviable.Conversion.opt_atom_upcase/0`.

### Examples

```elixir
iex> Enviable.fetch_env_as_atom!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("NAME", "fetch_env_as_atom!")
iex> Enviable.fetch_env_as_atom!("NAME")
:fetch_env_as_atom!
```

# `fetch_env_as_base16`
*since 1.4.0* 

```elixir
@spec fetch_env_as_base16(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:case, :upper | :lower | :mixed}
  | {atom(), term()}
]) :: :error | {:ok, term()}
```

Returns the value of an environment variable decoded from a base 16 string as
`{:ok, term()}` or `:error` if the environment variable is not set.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded.
  Must be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:case`: The value of `:case` passed to `Base.decode16/2`, which must be `:upper`,
  `:lower`, or `:mixed`. The default is `:mixed`.

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.encode16("RED", case: :lower))
iex> Enviable.fetch_env_as_base16("NAME")
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.encode16("RED", case: :lower))
iex> Enviable.fetch_env_as_base16("NAME", case: :lower)
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.encode16("RED", case: :lower))
iex> Enviable.fetch_env_as_base16("NAME", as: :string, case: :lower)
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.encode16("RED", case: :lower))
iex> Enviable.fetch_env_as_base16("NAME", as: :atom, case: :lower, downcase: true)
{:ok, :red}
```

# `fetch_env_as_base16!`
*since 1.4.0* 

```elixir
@spec fetch_env_as_base16!(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:case, :upper | :lower | :mixed}
  | {atom(), term()}
]) :: term()
```

Returns the value of an environment variable decoded from a base 16 string or raises an
exception if the variable is unset.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:case`: The value of `:case` passed to `Base.decode16/2`, which must be `:upper`,
  `:lower`, or `:mixed`. The default is `:mixed`.

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.encode16("RED", case: :lower))
iex> Enviable.fetch_env_as_base16!("NAME")
"RED"

iex> Enviable.put_env("NAME", Base.encode16("RED", case: :lower))
iex> Enviable.fetch_env_as_base16!("NAME", case: :lower)
"RED"

iex> Enviable.put_env("NAME", Base.encode16("RED", case: :lower))
iex> Enviable.fetch_env_as_base16!("NAME", as: :string, case: :lower)
"RED"

iex> Enviable.put_env("NAME", Base.encode16("RED", case: :lower))
iex> Enviable.fetch_env_as_base16!("NAME", as: :atom, case: :lower, downcase: true)
:red
```

# `fetch_env_as_base32`
*since 1.4.0* 

```elixir
@spec fetch_env_as_base32(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:case, :upper | :lower | :mixed}
  | {:padding, boolean()}
  | {atom(), term()}
]) :: :error | {:ok, term()}
```

Returns the value of an environment variable decoded from a base 32 string as
`{:ok, term()}` or `:error` if the environment variable is not set.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:case`: The value of `:case` passed to `Base.decode32/2`, which must be `:upper`,
  `:lower`, or `:mixed`. The default is `:mixed`.

- `:padding`: The boolean value of `:padding` passed to `Base.decode32/2`. The default
  is `false` (the opposite of `Base.decode32/2`).

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_base32("NAME")
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_base32("NAME", case: :lower)
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_base32("NAME", as: :string, case: :lower)
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_base32("NAME", as: :atom, case: :lower, downcase: true)
{:ok, :red}
```

# `fetch_env_as_base32!`
*since 1.4.0* 

```elixir
@spec fetch_env_as_base32!(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:case, :upper | :lower | :mixed}
  | {:padding, boolean()}
  | {atom(), term()}
]) :: term()
```

Returns the value of an environment variable decoded from a base 32 string or raises an
exception if the variable is unset.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:case`: The value of `:case` passed to `Base.decode32/2`, which must be `:upper`,
  `:lower`, or `:mixed`. The default is `:mixed`.

- `:padding`: The boolean value of `:padding` passed to `Base.decode32/2`. The default
  is `false` (the opposite of `Base.decode32/2`).

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_base32!("NAME")
"RED"

iex> Enviable.put_env("NAME", Base.encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_base32!("NAME", case: :lower)
"RED"

iex> Enviable.put_env("NAME", Base.encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_base32!("NAME", as: :string, case: :lower)
"RED"

iex> Enviable.put_env("NAME", Base.encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_base32!("NAME", as: :atom, case: :lower, downcase: true)
:red
```

# `fetch_env_as_base64`
*since 1.4.0* 

```elixir
@spec fetch_env_as_base64(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:ignore_whitespace, boolean()}
  | {:padding, boolean()}
  | {atom(), term()}
]) :: :error | {:ok, term()}
```

Returns the value of an environment variable decoded from a base 64 string as
`{:ok, term()}` or `:error` if the environment variable is not set.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:ignore_whitespace`: Whether to ignore whitespace values. The default is `true`,
  the opposite default for both `Base.decode64/2` and `Base.url_decode64/2`.

- `:padding`: The boolean value of `:padding` passed to `Base.decode64/2`. The default
  is `false` (the opposite of `Base.decode64/2`).

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.encode64("RED", padding: true))
iex> Enviable.fetch_env_as_base64("NAME")
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.encode64("RED", padding: true))
iex> Enviable.fetch_env_as_base64("NAME", padding: false)
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.encode64("RED", padding: true))
iex> Enviable.fetch_env_as_base64("NAME", as: :string, padding: true)
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.encode64("RED", padding: true))
iex> Enviable.fetch_env_as_base64("NAME", as: :atom, downcase: true, padding: false)
{:ok, :red}
```

# `fetch_env_as_base64!`
*since 1.4.0* 

```elixir
@spec fetch_env_as_base64!(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:case, :upper | :lower | :mixed}
  | {:padding, boolean()}
  | {atom(), term()}
]) :: term()
```

Returns the value of an environment variable decoded from a base 64 string or raises an
exception if the variable is unset.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:ignore_whitespace`: Whether to ignore whitespace values. The default is `true`, the
  opposite default for both `Base.decode64/2` and `Base.url_decode64/2`.

- `:padding`: The boolean value of `:padding` passed to `Base.decode64/2`. The default
  is `false` (the opposite of `Base.decode64/2`).

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.encode64("RED", padding: true))
iex> Enviable.fetch_env_as_base64!("NAME")
"RED"

iex> Enviable.put_env("NAME", Base.encode64("RED", padding: true))
iex> Enviable.fetch_env_as_base64!("NAME", padding: false)
"RED"

iex> Enviable.put_env("NAME", Base.encode64("RED", padding: true))
iex> Enviable.fetch_env_as_base64!("NAME", as: :string, padding: true)
"RED"

iex> Enviable.put_env("NAME", Base.encode64("RED", padding: true))
iex> Enviable.fetch_env_as_base64!("NAME", as: :atom, downcase: true, padding: false)
:red
```

# `fetch_env_as_boolean`
*since 1.3.0* 

```elixir
@spec fetch_env_as_boolean(String.t(), [
  Enviable.Conversion.opt_bool_downcase() | {:truthy | :falsy, [binary()]}
]) :: {:ok, boolean()} | :error
```

Returns the value of an environment variable converted to a `t:boolean/0` value as
`{:ok, boolean()}` or `:error` if the variable is unset.

This function will always result in a `t:boolean/0` value. Unless configured with
`truthy` or `falsy`, only the values `"1"` and `"true"` will be converted to `true` and
any other value will result in `false`.

### Options

- `:truthy`: a list of string values to be compared for truth values. If the value of
  the environment variable matches these values, `true` will be returned; other values
  will result in `false`. Mutually exclusive with `falsy`.

- `:falsy`: a list of string values to be compared for false values. If the value of the
  environment variable matches these values, `false` will be returned; other values will
  result in `true`. Mutually exclusive with `truthy`.

- `:downcase`: either `false`, `true`, or the mode parameter for `String.downcase/2`
  (`:default`, `:ascii`, `:greek`, or `:turkic`). The default is `:default`.

  The default `:downcase` value for boolean conversions can be changed at compile time
  through application configuration:

  ```elixir
  config :enviable, :boolean_downcase, true
  config :enviable, :boolean_downcase, :default
  config :enviable, :boolean_downcase, :ascii
  ```

### Examples

```elixir
iex> Enviable.fetch_env_as_boolean("UNSET")
:error

iex> Enviable.put_env("FLAG", "1")
iex> Enviable.fetch_env_as_boolean("FLAG")
{:ok, true}

iex> Enviable.put_env("FLAG", "something")
iex> Enviable.fetch_env_as_boolean("FLAG")
{:ok, false}

iex> Enviable.put_env("FLAG", "oui")
iex> Enviable.fetch_env_as_boolean("FLAG", truthy: ["oui"])
{:ok, true}

iex> Enviable.put_env("FLAG", "OUI")
iex> Enviable.fetch_env_as_boolean("FLAG", truthy: ["oui"])
{:ok, true}

iex> Enviable.put_env("FLAG", "OUI")
iex> Enviable.fetch_env_as_boolean("FLAG", truthy: ["oui"], downcase: false)
{:ok, false}

iex> Enviable.put_env("FLAG", "NON")
iex> Enviable.fetch_env_as_boolean("FLAG", falsy: ["non"])
{:ok, false}

iex> Enviable.put_env("FLAG", "NON")
iex> Enviable.fetch_env_as_boolean("FLAG", falsy: ["non"], downcase: false)
{:ok, true}
```

# `fetch_env_as_boolean!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_boolean!(String.t(), [
  Enviable.Conversion.opt_bool_downcase() | {:truthy | :falsy, [binary()]}
]) :: boolean()
```

Returns the value of an environment variable converted to a `t:boolean/0` value or
raises an exception if the variable is unset.

This function will always result in a `t:boolean/0` value. Unless configured with
`truthy` or `falsy`, only the values `"1"` and `"true"` will be converted to `true` and
any other value will result in `false`.

### Options

- `:truthy`: a list of string values to be compared for truth values. If the value of
  the environment variable matches these values, `true` will be returned; other values
  will result in `false`. Mutually exclusive with `falsy`.

- `:falsy`: a list of string values to be compared for false values. If the value of the
  environment variable matches these values, `false` will be returned; other values will
  result in `true`. Mutually exclusive with `truthy`.

- `:downcase`: either `false`, `true`, or the mode parameter for `String.downcase/2`
  (`:default`, `:ascii`, `:greek`, or `:turkic`). The default is `:default`.

  The default `:downcase` value for boolean conversions can be changed at compile time
  through application configuration:

  ```elixir
  config :enviable, :boolean_downcase, true
  config :enviable, :boolean_downcase, :default
  config :enviable, :boolean_downcase, :ascii
  ```

### Examples

```elixir
iex> Enviable.fetch_env_as_boolean!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("FLAG", "1")
iex> Enviable.fetch_env_as_boolean!("FLAG")
true

iex> Enviable.put_env("FLAG", "something")
iex> Enviable.fetch_env_as_boolean!("FLAG")
false

iex> Enviable.put_env("FLAG", "oui")
iex> Enviable.fetch_env_as_boolean!("FLAG", truthy: ["oui"])
true

iex> Enviable.put_env("FLAG", "OUI")
iex> Enviable.fetch_env_as_boolean!("FLAG", truthy: ["oui"])
true

iex> Enviable.put_env("FLAG", "OUI")
iex> Enviable.fetch_env_as_boolean!("FLAG", truthy: ["oui"], downcase: false)
false

iex> Enviable.put_env("FLAG", "NON")
iex> Enviable.fetch_env_as_boolean!("FLAG", falsy: ["non"])
false

iex> Enviable.put_env("FLAG", "NON")
iex> Enviable.fetch_env_as_boolean!("FLAG", falsy: ["non"], downcase: false)
true
```

# `fetch_env_as_charlist`
*since 1.3.0* 

```elixir
@spec fetch_env_as_charlist(String.t()) :: {:ok, charlist()} | :error
```

Returns the value of an environment variable converted to a `t:charlist/0` as
`{:ok, charlist()}` or `:error` if the variable is unset.

### Examples

```elixir
iex> Enviable.fetch_env_as_charlist("UNSET")
:error

iex> Enviable.put_env("NAME", "fetch_env_as")
iex> Enviable.fetch_env_as_charlist("NAME")
{:ok, ~c"fetch_env_as"}
```

# `fetch_env_as_charlist!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_charlist!(String.t()) :: charlist()
```

Returns the value of an environment variable converted to a `t:charlist/0` or raises an
exception if the variable is unset.

### Examples

```elixir
iex> Enviable.fetch_env_as_charlist!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("NAME", "fetch_env_as")
iex> Enviable.fetch_env_as_charlist!("NAME")
~c"fetch_env_as"
```

# `fetch_env_as_decimal`
*since 1.6.0* 

```elixir
@spec fetch_env_as_decimal(String.t()) :: {:ok, Decimal.t()} | :error
```

Returns the value of an environment variable converted to a `t:Decimal.t/0` value as
`{:ok, Decimal.t()}` or `:error` if the variable is unset.

### Examples

```elixir
iex> Enviable.fetch_env_as_decimal("UNSET")
:error

iex> Enviable.put_env("DECIMAL", "1")
iex> Enviable.fetch_env_as_decimal("DECIMAL")
{:ok, Decimal.new("1")}

iex> Enviable.put_env("DECIMAL", "ff")
iex> Enviable.fetch_env_as_decimal("DECIMAL")
** (Enviable.ConversionError) could not convert environment variable "DECIMAL" to type decimal
```

# `fetch_env_as_decimal!`
*since 1.6.0* 

```elixir
@spec fetch_env_as_decimal!(String.t()) :: Decimal.t()
```

Returns the value of an environment variable converted to a `t:Decimal.t/0` value or
`:error` if the variable is unset.

### Examples

```elixir
iex> Enviable.fetch_env_as_decimal!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("DECIMAL", "1")
iex> Enviable.fetch_env_as_decimal!("DECIMAL")
Decimal.new("1")

iex> Enviable.put_env("DECIMAL", "ff")
iex> Enviable.fetch_env_as_decimal!("DECIMAL")
** (Enviable.ConversionError) could not convert environment variable "DECIMAL" to type decimal
```

# `fetch_env_as_duration`
*since 2.2.0* 

```elixir
@spec fetch_env_as_duration(String.t()) :: {:ok, Duration.t()} | :error
```

Returns the value of an environment variable converted to a `t:Duration.t/0` as
`{:ok, Duration.t()}` or `:error` if the variable is unset.

### Examples

```elixir
iex> Enviable.fetch_env_as_duration("UNSET")
:error

iex> Enviable.put_env("DURATION", "PT30S")
iex> Enviable.fetch_env_as_duration("DURATION")
{:ok, %Duration{second: 30}}

iex> Enviable.put_env("DURATION", "PT1H30M")
iex> Enviable.fetch_env_as_duration("DURATION")
{:ok, %Duration{hour: 1, minute: 30}}
```

# `fetch_env_as_duration!`
*since 2.2.0* 

```elixir
@spec fetch_env_as_duration!(String.t()) :: Duration.t()
```

Returns the value of an environment variable converted to a `t:Duration.t/0` or raises
an exception if the variable is unset.

### Examples

```elixir
iex> Enviable.fetch_env_as_duration!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("DURATION", "PT30S")
iex> Enviable.fetch_env_as_duration!("DURATION")
%Duration{second: 30}

iex> Enviable.put_env("DURATION", "PT1H30M")
iex> Enviable.fetch_env_as_duration!("DURATION")
%Duration{hour: 1, minute: 30}
```

# `fetch_env_as_elixir`
*since 1.3.0* 

```elixir
@spec fetch_env_as_elixir(String.t()) :: {:ok, term()} | :error
```

Returns the value of an environment variable parsed and evaluated as Elixir code with
the result as `{:ok, term()}`, or `:error` if the environment variable is not set.

This can be used for tuples, complex map declarations, or other expressions difficult to
represent with other types. Longer code blocks should be encoded as base 64 text and
decoded with `{:base64, :elixir}`.

> #### Untrusted Input {: .error}
>
> This function parses (with `Code.string_to_quoted/1`) and evaluates (with
> `Code.eval_quoted/1`) elixir code from environment variables in the context of your
> application. Do not use this with untrusted input.

### Examples

```elixir
iex> Enviable.fetch_env_as_elixir("UNSET")
:error

iex> Enviable.put_env("TERM", "11000..11100//3")
iex> Enviable.fetch_env_as_elixir("TERM")
{:ok, 11000..11100//3}
```

# `fetch_env_as_elixir!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_elixir!(String.t()) :: term()
```

Returns the value of an environment variable parsed and evaluated as Elixir code with
the result , or raises an exception if the environment variable is not set.

This can be used for tuples, complex map declarations, or other expressions difficult to
represent with other types. Longer code blocks should be encoded as base 64 text and
decoded with `{:base64, :elixir}`.

> #### Untrusted Input {: .error}
>
> This function parses (with `Code.string_to_quoted/1`) and evaluates (with
> `Code.eval_quoted/1`) elixir code from environment variables in the context of your
> application. Do not use this with untrusted input.

### Examples

```elixir
iex> Enviable.fetch_env_as_elixir!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("TERM", "11000..11100//3")
iex> Enviable.fetch_env_as_elixir!("TERM")
11000..11100//3
```

# `fetch_env_as_erlang`
*since 1.3.0* 

```elixir
@spec fetch_env_as_erlang(String.t()) :: {:ok, term()} | :error
```

Returns the value of an environment variable parsed and evaluated as Erlang code with
the result as `{:ok, term()}`, or `:error` if the environment variable is not set.

This can be used for tuples, complex map declarations, or other expressions difficult to
represent with other types. Longer code blocks should be encoded as base 64 text and
decoded with `{:base64, :erlang}`.

> #### Untrusted Input {: .error}
>
> This function parses (with `:erl_scan.string/1`) and evaluates (with
> `:erl_parse.parse_term/1`) Erlang code from environment variables in the context of
> your application. Do not use this with untrusted input.

### Examples

```elixir
iex> Enviable.fetch_env_as_erlang("UNSET")
:error

iex> Enviable.put_env("TERM", "{ok, true}.")
iex> Enviable.fetch_env_as_erlang("TERM")
{:ok, {:ok, true}}
```

# `fetch_env_as_erlang!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_erlang!(String.t()) :: term()
```

Returns the value of an environment variable parsed and evaluated as Erlang code with
the result , or raises an exception if the environment variable is not set.

This can be used for tuples, complex map declarations, or other expressions difficult to
represent with other types. Longer code blocks should be encoded as base 64 text and
decoded with `{:base64, :erlang}`.

> #### Untrusted Input {: .error}
>
> This function parses (with `:erl_scan.string/1`) and evaluates (with
> `:erl_parse.parse_term/1`) Erlang code from environment variables in the
> context of your application. Do not use this with untrusted input.

### Examples

```elixir
iex> Enviable.fetch_env_as_erlang!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("TERM", "{ok, true}.")
iex> Enviable.fetch_env_as_erlang!("TERM")
{:ok, true}
```

# `fetch_env_as_float`
*since 1.3.0* 

```elixir
@spec fetch_env_as_float(String.t()) :: {:ok, float()} | :error
```

Returns the value of an environment variable converted to a `t:float/0` value as
`{:ok, float()}` or `:error` if the variable is unset.

### Examples

```elixir
iex> Enviable.fetch_env_as_float("UNSET")
:error

iex> Enviable.put_env("FLOAT", "1")
iex> Enviable.fetch_env_as_float("FLOAT")
{:ok, 1.0}

iex> Enviable.put_env("FLOAT", "ff")
iex> Enviable.fetch_env_as_float("FLOAT")
** (Enviable.ConversionError) could not convert environment variable "FLOAT" to type float
```

# `fetch_env_as_float!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_float!(String.t()) :: float()
```

Returns the value of an environment variable converted to a `t:float/0` value or
`:error` if the variable is unset.

### Examples

```elixir
iex> Enviable.fetch_env_as_float!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("FLOAT", "1")
iex> Enviable.fetch_env_as_float!("FLOAT")
1.0

iex> Enviable.put_env("FLOAT", "ff")
iex> Enviable.fetch_env_as_float!("FLOAT")
** (Enviable.ConversionError) could not convert environment variable "FLOAT" to type float
```

# `fetch_env_as_hex32`
*since 1.4.0* 

```elixir
@spec fetch_env_as_hex32(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:case, :upper | :lower | :mixed}
  | {:padding, boolean()}
  | {atom(), term()}
]) :: :error | {:ok, term()}
```

Returns the value of an environment variable decoded from a base 32 hex encoded string
as `{:ok, term()}` or `:error` if the environment variable is not set.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:case`: The value of `:case` passed to `Base.hex_decode32/2`, which must be `:upper`,
  `:lower`, or `:mixed`. The default is `:mixed`.

- `:padding`: The boolean value of `:padding` passed to `hex.decode32/2`. The default
  is `false` (the opposite of `Base.hex_decode32/2`).

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.hex_encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_hex32("NAME")
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.hex_encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_hex32("NAME", case: :lower)
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.hex_encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_hex32("NAME", as: :string, case: :lower)
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.hex_encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_hex32("NAME", as: :atom, case: :lower, downcase: true)
{:ok, :red}
```

# `fetch_env_as_hex32!`
*since 1.4.0* 

```elixir
@spec fetch_env_as_hex32!(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:case, :upper | :lower | :mixed}
  | {:padding, boolean()}
  | {atom(), term()}
]) :: term()
```

Returns the value of an environment variable decoded from a base 32 hex encoded string
or raises an exception if the variable is unset.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:case`: The value of `:case` passed to `Base.hex_decode32/2`, which must be `:upper`,
  `:lower`, or `:mixed`. The default is `:mixed`.

- `:padding`: The boolean value of `:padding` passed to `hex.decode32/2`. The default is
  `false` (the opposite of `Base.hex_decode32/2`).

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.hex_encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_hex32!("NAME")
"RED"

iex> Enviable.put_env("NAME", Base.hex_encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_hex32!("NAME", case: :lower)
"RED"

iex> Enviable.put_env("NAME", Base.hex_encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_hex32!("NAME", as: :string, case: :lower)
"RED"

iex> Enviable.put_env("NAME", Base.hex_encode32("RED", case: :lower))
iex> Enviable.fetch_env_as_hex32!("NAME", as: :atom, case: :lower, downcase: true)
:red
```

# `fetch_env_as_integer`
*since 1.3.0* 

```elixir
@spec fetch_env_as_integer(String.t(), [{:base, 2..36}]) :: {:ok, integer()} | :error
```

Returns the value of an environment variable converted to a `t:integer/0` value as
`{:ok, integer()}` or `:error` if the variable is unset.

### Options

- `:base`: The base (`2..36`) for integer conversion. Defaults to base `10` like
  `String.to_integer/2`.

Failure to parse the value of the environment variable will result in an exception.

### Examples

```elixir
iex> Enviable.fetch_env_as_integer("UNSET")
:error

iex> Enviable.put_env("PORT", "5432")
iex> Enviable.fetch_env_as_integer("PORT")
{:ok, 5432}

iex> Enviable.put_env("PORT", "18eb")
iex> Enviable.fetch_env_as_integer("PORT")
** (Enviable.ConversionError) could not convert environment variable "PORT" to type integer

iex> Enviable.put_env("PORT", "18EB")
iex> Enviable.fetch_env_as_integer("PORT", base: 16)
{:ok, 6379}
```

# `fetch_env_as_integer!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_integer!(String.t(), [{:base, 2..36}]) :: integer()
```

Returns the value of an environment variable converted to a `t:integer/0` value or
raises an exception if the variable is unset.

### Options

- `:base`: The base (`2..36`) for integer conversion. Defaults to base `10` like
  `String.to_integer/2`.

Failure to parse the value of the environment variable will result in an exception.

### Examples

```elixir
iex> Enviable.fetch_env_as_integer!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("PORT", "5432")
iex> Enviable.fetch_env_as_integer!("PORT")
5432

iex> Enviable.put_env("PORT", "18eb")
iex> Enviable.fetch_env_as_integer!("PORT")
** (Enviable.ConversionError) could not convert environment variable "PORT" to type integer

iex> Enviable.put_env("PORT", "18EB")
iex> Enviable.fetch_env_as_integer!("PORT", base: 16)
6379
```

# `fetch_env_as_json`
*since 1.3.0* 

```elixir
@spec fetch_env_as_json(String.t(), [
  {:engine, module() | (String.t() -&gt; Enviable.Conversion.json())}
]) ::
  {:ok, Enviable.Conversion.json()} | :error
```

Returns the value of an environment variable converted to
a `t:Enviable.Conversion.json/0` value as `{:ok, Conversion.json()}` or `:error` if the
variable is unset.

### Options

- `:engine`: The JSON engine to use. May be provided as a `t:module/0` (which must
  export `decode/1`), an arity 1 function, or a `t:mfa/0` tuple. When provided with
  a `t:mfa/0`, the variable value will be passed as the first parameter.

  If the engine produces `{:ok, json_value}` or an expected JSON type result, it will be
  considered successful. Any other result will be treated as failure.

  The default JSON engine is `JSON` if Elixir's `m:JSON` is available, `:json` if the
  Erlang/OTP `m::json` module is available (Erlang/OTP 27+) or provided by
  [json_polyfill][jp]. Otherwise, [Jason][jason] is the default engine. This choice may
  be overridden with application configuration, as this example shows using
  [Thoas][thoas].

  ```elixir
  import Config

  config :enviable, :json_engine, :thoas
  ```

[jp]: https://hexdocs.pm/json_polyfill/readme.html
[jason]: https://hexdocs.pm/jason/readme.html
[thoas]: https://hexdocs.pm/thoas/readme.html

### Examples

```elixir
iex> Enviable.fetch_env_as_json("UNSET")
:error

iex> Enviable.put_env("JSON", ~S|[{"foo":"bar"}]|)
iex> Enviable.fetch_env_as_json("JSON")
{:ok, [%{"foo" => "bar"}]}

iex> Enviable.put_env("JSON", "ff")
iex> Enviable.fetch_env_as_json("JSON")
** (Enviable.ConversionError) could not convert environment variable "JSON" to type json
```

# `fetch_env_as_json!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_json!(String.t(), [
  {:engine, module() | (String.t() -&gt; Enviable.Conversion.json())}
]) ::
  Enviable.Conversion.json()
```

Returns the value of an environment variable converted to
a `t:Enviable.Conversion.json/0` value or raises an exception if the variable is unset.

### Options

- `:engine`: The JSON engine to use. May be provided as a `t:module/0` (which must
  export `decode/1`), an arity 1 function, or a `t:mfa/0` tuple. When provided with
  a `t:mfa/0`, the variable value will be passed as the first parameter.

  If the engine produces `{:ok, json_value}` or an expected JSON type result, it will be
  considered successful. Any other result will be treated as failure.

  The default JSON engine is `JSON` if Elixir's `m:JSON` is available, `:json` if the
  Erlang/OTP `m::json` module is available (Erlang/OTP 27+) or provided by
  [json_polyfill][jp]. Otherwise, [Jason][jason] is the default engine. This choice may
  be overridden with application configuration, as this example shows using
  [Thoas][thoas].

  ```elixir
  import Config

  config :enviable, :json_engine, :thoas
  ```

[jp]: https://hexdocs.pm/json_polyfill/readme.html
[jason]: https://hexdocs.pm/jason/readme.html
[thoas]: https://hexdocs.pm/thoas/readme.html

### Examples

```elixir
iex> Enviable.fetch_env_as_json!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("JSON", ~S|[{"foo":"bar"}]|)
iex> Enviable.fetch_env_as_json!("JSON")
[%{"foo" => "bar"}]

iex> Enviable.put_env("JSON", "ff")
iex> Enviable.fetch_env_as_json!("JSON")
** (Enviable.ConversionError) could not convert environment variable "JSON" to type json
```

# `fetch_env_as_list`
*since 1.4.0* 

```elixir
@spec fetch_env_as_list(String.t(), [as_list_opt()]) :: :error | {:ok, list()}
```

Returns the value of an environment variable parsed as a delimiter-separated list.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Each entry must conform to the permitted
  type provided in `:as`.

- `:delimiter`: The delimiter used to separate the list. This must be a pattern accepted
  by `String.split/3` (a string, a list of strings, a compiled binary pattern, or
  a regular expression). Defaults to `","`.

- `:parts`: The maximum number of parts to split into (`t:pos_integer/0` or
  `:infinity`). Passed to `String.split/3`.

- `:trim`: A boolean option whether empty entries should be omitted.

When the pattern is a regular expression, `Regex.split/3` options are also supported:

- `:on`: specifies which captures to split the string on, and in what order. Defaults to
  `:first` which means captures inside the regex do not affect the splitting process.

- `:include_captures`: when true, includes in the result the matches of the regular
  expression. The matches are not counted towards the maximum number of parts if
  combined with the `:parts` option. Defaults to `false`.

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("LIST", "1,2,3")
iex> Enviable.fetch_env_as_list("LIST")
{:ok, ["1", "2", "3"]}
iex> Enviable.fetch_env_as_list("LIST", as: :integer)
{:ok, [1, 2, 3]}

iex> Enviable.put_env("LIST", "1;2;3")
iex> Enviable.fetch_env_as_list("LIST", delimiter: ";")
{:ok, ["1", "2", "3"]}
iex> Enviable.fetch_env_as_list("LIST", as: :integer, delimiter: ";")
{:ok, [1, 2, 3]}
```

# `fetch_env_as_list!`
*since 1.4.0* 

```elixir
@spec fetch_env_as_list!(String.t(), [as_list_opt()]) :: list()
```

Returns the value of an environment variable parsed as a delimiter-separated list.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Each entry must conform to the permitted
  type provided in `:as`.

- `:delimiter`: The delimiter used to separate the list. This must be a pattern accepted
  by `String.split/3` (a string, a list of strings, a compiled binary pattern, or
  a regular expression). Defaults to `","`.

- `:parts`: The maximum number of parts to split into (`t:pos_integer/0` or
  `:infinity`). Passed to `String.split/3`.

- `:trim`: A boolean option whether empty entries should be omitted.

When the pattern is a regular expression, `Regex.split/3` options are also supported:

- `:on`: specifies which captures to split the string on, and in what order. Defaults to
  :first which means captures inside the regex do not affect the splitting process.

- `:include_captures`: when true, includes in the result the matches of the regular
  expression. The matches are not counted towards the maximum number of parts if
  combined with the `:parts` option. Defaults to `false`.

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("LIST", "1,2,3")
iex> Enviable.fetch_env_as_list!("LIST")
["1", "2", "3"]
iex> Enviable.fetch_env_as_list!("LIST", as: :integer)
[1, 2, 3]

iex> Enviable.put_env("LIST", "1;2;3")
iex> Enviable.fetch_env_as_list!("LIST", delimiter: ";")
["1", "2", "3"]
iex> Enviable.fetch_env_as_list!("LIST", as: :integer, delimiter: ";")
[1, 2, 3]
```

# `fetch_env_as_log_level`
*since 1.3.0* 

```elixir
@spec fetch_env_as_log_level(String.t()) ::
  {:ok, Enviable.Conversion.log_level()} | :error
```

Returns the value of an environment variable converted to a log level `t:atom/0` for
`Logger.configure/1` as `{:ok, atom()}` or `:error` if the variable is unset.

### Examples

```elixir
iex> Enviable.fetch_env_as_log_level("UNSET")
:error

iex> Enviable.put_env("LOG_LEVEL", "critical")
iex> Enviable.fetch_env_as_log_level("LOG_LEVEL")
{:ok, :critical}
```

# `fetch_env_as_log_level!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_log_level!(String.t()) :: Enviable.Conversion.log_level()
```

Returns the value of an environment variable converted to a log level `t:atom/0` for
`Logger.configure/1` or raises an exception if the variable is unset.

### Examples

```elixir
iex> Enviable.fetch_env_as_log_level!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("LOG_LEVEL", "critical")
iex> Enviable.fetch_env_as_log_level!("LOG_LEVEL")
:critical
```

# `fetch_env_as_module`
*since 1.3.0* 

```elixir
@spec fetch_env_as_module(String.t(), [{:allowed, [module()]}]) ::
  {:ok, module()} | :error
```

Returns the value of an environment variable converted to `t:module/0` as
`{:ok, module()}` or `:error` if the variable is unset.

> #### Untrusted Input {: .warning}
>
> This conversion routine uses `Module.concat/1` and may result in atom exhaustion if
> used without the `:allowed` option. See [Preventing atom exhaustion](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/atom_exhaustion) from the Security Working
> Group of the Erlang Ecosystem Foundation.

### Options

- `:allowed`: A list of `t:module/0` values indicating permitted module and used as
  a lookup table, if present. Any value not found will result in an exception.

### Examples

```elixir
iex> Enviable.fetch_env_as_module("UNSET")
:error

iex> Enviable.put_env("NAME", "Enviable")
iex> Enviable.fetch_env_as_module("NAME")
{:ok, Elixir.Enviable}
```

# `fetch_env_as_module!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_module!(String.t(), [{:allowed, [module()]}]) :: module()
```

Returns the value of an environment variable converted to `t:module/0` or raises an
exception if the variable is unset.

> #### Untrusted Input {: .warning}
>
> This conversion routine uses `Module.concat/1` and may result in atom exhaustion if
> used without the `:allowed` option. See [Preventing atom exhaustion](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/atom_exhaustion) from the Security Working
> Group of the Erlang Ecosystem Foundation.

### Options

- `:allowed`: A list of `t:module/0` values indicating permitted module and used as
  a lookup table, if present. Any value not found will result in an exception.

### Examples

```elixir
iex> Enviable.fetch_env_as_module!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("NAME", "Enviable")
iex> Enviable.fetch_env_as_module!("NAME")
Elixir.Enviable
```

# `fetch_env_as_pem`
*since 1.3.0* 

```elixir
@spec fetch_env_as_pem(String.t(), [{:filter, boolean() | :cert | :key}]) ::
  {:ok, Enviable.Conversion.pem()} | :error
```

Returns the value of an environment variable converted from a PEM string through
`:public_key.pem_decode/1` as `{:ok, value}` or `:error` if the variable is unset.

### Options

- `:filter`: Filters the output of `:public_key.pem_decode/1`. Permitted values are
  `false`, `true`, `:cert`, or `:key`. The default is `true`.

  - `false`: the list is returned without processing, suitable for further processing
    with `:public_key.pem_entry_decode/2`.

  - `true`: returns the first unencrypted `:PrivateKeyInfo` found, a list of unencrypted
    `:Certificate` records, or an empty list.

  - `:cert`: returns a list of unencrypted `:Certificate` records or raises an
    exception if none are found.

  - `:key`: returns the first unencrypted `:PrivateKeyIinfo` record or raises an
    exception if one is not found.

### Examples

```elixir
iex> Enviable.fetch_env_as_pem("UNSET")
:error

iex> Enviable.put_env("PEM", "")
iex> Enviable.fetch_env_as_pem("PEM")
{:ok, []}
```

# `fetch_env_as_pem!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_pem!(String.t(), [{:filter, boolean() | :cert | :key}]) ::
  Enviable.Conversion.pem()
```

Returns the value of an environment variable converted from a PEM string through
`:public_key.pem_decode/1` or raises an exception if the variable is unset.

### Options

- `:filter`: Filters the output of `:public_key.pem_decode/1`. Permitted values are
  `false`, `true`, `:cert`, or `:key`. The default is `true`.

  - `false`: the list is returned without processing, suitable for further processing
    with `:public_key.pem_entry_decode/2`.

  - `true`: returns the first unencrypted `:PrivateKeyInfo` found, a list of unencrypted
    `:Certificate` records, or an empty list.

  - `:cert`: returns a list of unencrypted `:Certificate` records or raises an
    exception if none are found.

  - `:key`: returns the first unencrypted `:PrivateKeyIinfo` record or raises an
    exception if one is not found.

### Examples

```elixir
iex> Enviable.fetch_env_as_pem!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("PEM", "")
iex> Enviable.fetch_env_as_pem!("PEM")
[]
```

# `fetch_env_as_safe_atom`
*since 1.3.0* 

```elixir
@spec fetch_env_as_safe_atom(String.t(), [
  {:allowed, [atom()]} | Enviable.Conversion.opt_atom_downcase()
]) :: {:ok, atom()} | :error
```

Returns the value of an environment variable converted to an existing `t:atom/0` as
`{:ok, atom()}` or `:error` if the variable is unset.

> #### Untrusted Input {: .warning}
>
> This conversion routine uses `String.to_existing_atom/1` which will result in an
> exception if the resulting atom is not already known and if used without the
> `:allowed` option. See [Preventing atom exhaustion](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/atom_exhaustion) from the Security Working Group of the
> Erlang Ecosystem Foundation.

### Options

- `:allowed`: A list of `t:atom/0` values indicating permitted atoms and used as
  a lookup table, if present. Any value not found will result in an exception.

- `:downcase`: See `t:Enviable.Conversion.opt_atom_downcase/0`.

- `:upcase`: See `t:Enviable.Conversion.opt_atom_upcase/0`.

### Examples

```elixir
iex> Enviable.fetch_env_as_safe_atom("UNSET")
:error

iex> Enviable.put_env("NAME", "FETCH_ENV_AS_SAFE_ATOM")
iex> Enviable.fetch_env_as_safe_atom("NAME", downcase: true)
{:ok, :fetch_env_as_safe_atom}
```

# `fetch_env_as_safe_atom!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_safe_atom!(String.t(), [
  {:allowed, [atom()]} | Enviable.Conversion.opt_atom_downcase()
]) :: atom()
```

Returns the value of an environment variable converted to an existing `t:atom/0` or
raises an exception if the variable is unset.

> #### Untrusted Input {: .warning}
>
> This conversion routine uses `String.to_existing_atom/1` which will result in an
> exception if the resulting atom is not already known and if used without the
> `:allowed` option. See [Preventing atom exhaustion](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/atom_exhaustion) from the Security Working Group of the
> Erlang Ecosystem Foundation.

### Options

- `:allowed`: A list of `t:atom/0` values indicating permitted atoms and used as
  a lookup table, if present. Any value not found will result in an exception.

- `:downcase`: See `t:Enviable.Conversion.opt_atom_downcase/0`.

- `:upcase`: See `t:Enviable.Conversion.opt_atom_upcase/0`.

### Examples

```elixir
iex> Enviable.fetch_env_as_safe_atom!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("NAME", "FETCH_ENV_AS_SAFE_ATOM!")
iex> Enviable.fetch_env_as_safe_atom!("NAME", downcase: true)
:fetch_env_as_safe_atom!
```

# `fetch_env_as_safe_module`
*since 1.3.0* 

```elixir
@spec fetch_env_as_safe_module(String.t(), [{:allowed, [module()]}]) ::
  {:ok, module()} | :error
```

Returns the value of an environment variable converted to `t:module/0` as
`{:ok, module()}` or `:error` if the variable is unset.  The resulting `t:module/0` must
already exist.

> #### Untrusted Input {: .warning}
>
> This conversion routine uses `Module.safe_concat/1` which will result in an exception
> if the resulting module is not already known and if used without the `:allowed`
> option. See [Preventing atom exhaustion](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/atom_exhaustion) from the Security Working Group of the Erlang Ecosystem
> Foundation.

### Options

- `:allowed`: A list of `t:module/0` values indicating permitted module and used as
  a lookup table, if present. Any value not found will result in an exception.

### Examples

```elixir
iex> Enviable.fetch_env_as_safe_module("UNSET")
:error

iex> Enviable.put_env("NAME", "Enviable")
iex> Enviable.fetch_env_as_safe_module("NAME")
{:ok, Elixir.Enviable}
```

# `fetch_env_as_safe_module!`
*since 1.3.0* 

```elixir
@spec fetch_env_as_safe_module!(String.t(), [{:allowed, [module()]}]) :: module()
```

Returns the value of an environment variable converted to `t:module/0` or raises an
exception if the variable is unset. The resulting `t:module/0` must already exist.

> #### Untrusted Input {: .warning}
>
> This conversion routine uses `Module.safe_concat/1` which will result in an exception
> if the resulting module is not already known and if used without the `:allowed`
> option. See [Preventing atom exhaustion](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/atom_exhaustion) from the Security Working Group of the Erlang Ecosystem
> Foundation.

### Options

- `:allowed`: A list of `t:module/0` values indicating permitted module and used as
  a lookup table, if present. Any value not found will result in an exception.

### Examples

```elixir
iex> Enviable.fetch_env_as_safe_module!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("NAME", "Enviable")
iex> Enviable.fetch_env_as_safe_module!("NAME")
Elixir.Enviable
```

# `fetch_env_as_timeout`
*since 1.7.0* 

```elixir
@spec fetch_env_as_timeout(String.t()) :: {:ok, timeout()} | :error
```

Returns the value of an environment variable converted to a millisecond `t:timeout/0` as
`{:ok, timeout()}` or `:error` if the variable is unset.

### Timeout Values

Timeout values are specified as non-negative integer values with optional suffixes or
the word `infinity`. The integer part may have underscores (`_`) separating digits like
Elixir itself.

If no suffix is present, the value is in milliseconds. Supported suffixes are:

- `week`, `weeks`, `w`: the number of weeks (always 7 days)

- `day`, `days`, `d`: the number of days (always 24 hours)

- `hour`, `hours`, `h`: the number of hours (always 60 minutes)

- `minute`, `minutes`, `m`: the number of minutes (always 60 seconds)

- `second`, `seconds`, `s`: the number of seconds (always 1000 milliseconds)

- `millisecond`, `milliseconds`, `ms`: the number of milliseconds

Suffixes may be present with or without a space (`30s` and `30 s` are the same value),
and multiple timeouts may be chained (`1h 30m`), but may not be duplicated. See
`Kernel.to_timeout/1` for more details.

Only lowercase suffixes are supported.

### Examples

```elixir
iex> Enviable.fetch_env_as_timeout("UNSET")
:error

iex> Enviable.put_env("TIMEOUT", "infinity")
iex> Enviable.fetch_env_as_timeout("TIMEOUT")
{:ok, :infinity}

iex> Enviable.put_env("TIMEOUT", "3_0 seconds")
iex> Enviable.fetch_env_as_timeout("TIMEOUT")
{:ok, 30000}
```

# `fetch_env_as_timeout!`
*since 1.7.0* 

```elixir
@spec fetch_env_as_timeout!(String.t()) :: timeout()
```

Returns the value of an environment variable converted to a millisecond `t:timeout/0` or
raises an exception if the variable is unset.

### Timeout Values

Timeout values are specified as non-negative integer values with optional suffixes or
the word `infinity`. The integer part may have underscores (`_`) separating digits like
Elixir itself.

If no suffix is present, the value is in milliseconds. Supported suffixes are:

- `week`, `weeks`, `w`: the number of weeks (always 7 days)

- `day`, `days`, `d`: the number of days (always 24 hours)

- `hour`, `hours`, `h`: the number of hours (always 60 minutes)

- `minute`, `minutes`, `m`: the number of minutes (always 60 seconds)

- `second`, `seconds`, `s`: the number of seconds (always 1000 milliseconds)

- `millisecond`, `milliseconds`, `ms`: the number of milliseconds

Suffixes may be present with or without a space (`30s` and `30 s` are the same value),
and multiple timeouts may be chained (`1h 30m`), but may not be duplicated. See
`Kernel.to_timeout/1` for more details.

Only lowercase suffixes are supported.

### Examples

```elixir
iex> Enviable.fetch_env_as_timeout!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("TIMEOUT", "infinity")
iex> Enviable.fetch_env_as_timeout!("TIMEOUT")
:infinity

iex> Enviable.put_env("TIMEOUT", "3_0 seconds")
iex> Enviable.fetch_env_as_timeout!("TIMEOUT")
30000
```

# `fetch_env_as_url_base64`
*since 1.4.0* 

```elixir
@spec fetch_env_as_url_base64(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:ignore_whitespace, boolean()}
  | {:padding, boolean()}
  | {atom(), term()}
]) :: :error | {:ok, term()}
```

Returns the value of an environment variable decoded from a URL-safe base 64 string as
`{:ok, term()}` or `:error` if the environment variable is not set.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:ignore_whitespace`: Whether to ignore whitespace values. The default is `true`, the
  opposite default for both `Base.decode64/2` and `Base.url_decode64/2`.

- `:padding`: The boolean value of `:padding` passed to `Base.decode64/2`. The default
  is `false` (the opposite of `Base.decode64/2`).

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.url_encode64("RED", padding: true))
iex> Enviable.fetch_env_as_url_base64("NAME")
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.url_encode64("RED", padding: true))
iex> Enviable.fetch_env_as_url_base64("NAME", padding: false)
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.url_encode64("RED", padding: true))
iex> Enviable.fetch_env_as_url_base64("NAME", as: :string, padding: true)
{:ok, "RED"}

iex> Enviable.put_env("NAME", Base.url_encode64("RED", padding: true))
iex> Enviable.fetch_env_as_url_base64("NAME", as: :atom, downcase: true, padding: false)
{:ok, :red}
```

# `fetch_env_as_url_base64!`
*since 1.4.0* 

```elixir
@spec fetch_env_as_url_base64!(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:case, :upper | :lower | :mixed}
  | {:padding, boolean()}
  | {atom(), term()}
]) :: term()
```

Returns the value of an environment variable decoded from a URL-safe base 64 string or
raises an exception if the variable is unset.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:ignore_whitespace`: Whether to ignore whitespace values. The default is `true`, the
  opposite default for both `Base.decode64/2` and `Base.url_decode64/2`.

- `:padding`: The boolean value of `:padding` passed to `Base.decode64/2`. The default
  is `false` (the opposite of `Base.decode64/2`).

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.url_encode64("RED", padding: true))
iex> Enviable.fetch_env_as_url_base64!("NAME")
"RED"

iex> Enviable.put_env("NAME", Base.url_encode64("RED", padding: true))
iex> Enviable.fetch_env_as_url_base64!("NAME", padding: false)
"RED"

iex> Enviable.put_env("NAME", Base.url_encode64("RED", padding: true))
iex> Enviable.fetch_env_as_url_base64!("NAME", as: :string, padding: true)
"RED"

iex> Enviable.put_env("NAME", Base.url_encode64("RED", padding: true))
iex> Enviable.fetch_env_as_url_base64!("NAME", as: :atom, downcase: true, padding: false)
:red
```

# `get_env_as`
*since 1.1.0* 

```elixir
@spec get_env_as(String.t(), Enviable.Conversion.conversion(), keyword()) ::
  nil | term()
```

Returns the value of an environment variable converted to the target `type` or a default
value if the variable is unset. If no `default` is provided, `nil` is returned (unless
converting to `:boolean`, which will return `false`).

Supported primitive conversions are:

- `:atom` (`t:Enviable.Conversion.convert_atom/0`, `get_env_as_atom/2`)

- `:boolean` (`t:Enviable.Conversion.convert_boolean/0`, `get_env_as_boolean/2`)

- `:charlist` (`t:Enviable.Conversion.convert_charlist/0`, `get_env_as_charlist/2`)

- `:decimal` (`t:Enviable.Conversion.convert_decimal/0`, `get_env_as_decimal/2`)

- `:duration` (`t:Enviable.Conversion.convert_duration/0`, `get_env_as_duration/2`)

- `:elixir` (`t:Enviable.Conversion.convert_elixir/0`, `get_env_as_elixir/1`)

- `:erlang` (`t:Enviable.Conversion.convert_erlang/0`, `get_env_as_erlang/1`)

- `:float` (`t:Enviable.Conversion.convert_float/0`, `get_env_as_float/2`)

- `:integer` (`t:Enviable.Conversion.convert_integer/0`, `get_env_as_integer/2`)

- `:json` (`t:Enviable.Conversion.convert_json/0`, `get_env_as_json/2`)

- `:log_level` (`t:Enviable.Conversion.convert_log_level/0`, `get_env_as_log_level/2`)

- `:module` (`t:Enviable.Conversion.convert_module/0`, `get_env_as_module/2`)

- `:pem` (`t:Enviable.Conversion.convert_pem/0`, `get_env_as_pem/2`)

- `:safe_atom` (`t:Enviable.Conversion.convert_safe_atom/0`, `get_env_as_safe_atom/2`)

- `:safe_module` (`t:Enviable.Conversion.convert_safe_module/0`,
  `get_env_as_safe_module/2`)

- `:timeout` (`t:Enviable.Conversion.convert_timeout/0`, `get_env_as_timeout/2`)

Supported encoded conversions are:

- `:base16` (`t:Enviable.Conversion.encoded_base16/0`, `get_env_as_base16/2`)

- `:base32` (`t:Enviable.Conversion.encoded_base32/0`, `get_env_as_base32/2`)

- `:base64`, `:url_base64` (`t:Enviable.Conversion.encoded_base64/0`,
  `get_env_as_base64/2`, `get_env_as_url_base64/2`)

- `:hex32` (`t:Enviable.Conversion.encoded_hex32/0`, `get_env_as_hex32/2`)

- `:list` (`t:Enviable.Conversion.encoded_list/0`, `get_env_as_list/2`)

See `Enviable.Conversion` for supported type conversions and options.

### Value Conversion and Default Values

Value conversion will be applied only to values contained in the requested environment
variable. Default values are used only when the environment variable is unset. There is
a meaningful difference between an unset environment variable (where `System.get_env/1`
will return `nil`) and an empty environment variable (where `System.get_env/1` returns
`""`).

```console
$ FOO= elixir -e 'IO.inspect([foo: System.get_env("FOO"), bar: System.get_env("BAR")])'
[foo: "", bar: nil]
```

### Examples

```elixir
iex> Enviable.get_env_as("UNSET", :atom)
nil

iex> Enviable.get_env_as("UNSET", :float)
nil

iex> Enviable.get_env_as("UNSET", :base16)
nil

iex> Enviable.put_env("NAME", "get_env_as")
iex> Enviable.get_env_as("NAME", :atom)
:get_env_as

iex> Enviable.put_env("NAME", "GET_ENV_AS")
iex> Enviable.get_env_as("NAME", :safe_atom, downcase: true)
:get_env_as

iex> Enviable.get_env_as("FLOAT", :float, default: "3.5")
3.5

iex> Enviable.get_env_as("FLOAT", :float, default: 3.5)
3.5

iex> Enviable.put_env("FLOAT", "3")
iex> Enviable.get_env_as("FLOAT", :float)
3.0

iex> Enviable.put_env("FLOAT", "3.1")
iex> Enviable.get_env_as("FLOAT", :float)
3.1

iex> red = Base.encode16("RED", case: :lower)
iex> Enviable.put_env("NAME", red)
iex> Enviable.get_env_as("NAME", :base16, case: :lower)
"RED"
iex> Enviable.get_env_as("NAME", {:base16, :string}, case: :lower)
"RED"
iex> Enviable.get_env_as("NAME", {:base16, :atom}, case: :lower, downcase: true)
:red

iex> Enviable.put_env("LIST", "1,2,3")
iex> Enviable.get_env_as("LIST", :list)
["1", "2", "3"]
iex> Enviable.get_env_as("LIST", {:list, :integer})
[1, 2, 3]

iex> Enviable.put_env("LIST", "1;2;3")
iex> Enviable.get_env_as("LIST", :list, delimiter: ";")
["1", "2", "3"]
iex> Enviable.get_env_as("LIST", {:list, :integer}, delimiter: ";")
[1, 2, 3]
```

# `get_env_as_atom`
*since 1.3.0* 

```elixir
@spec get_env_as_atom(
  String.t(),
  atom()
  | binary()
  | [
      {:allowed, [atom()]}
      | {:default, atom() | binary()}
      | Enviable.Conversion.opt_atom_downcase()
    ]
) :: nil | atom()
```

Returns the value of an environment variable converted to `t:atom/0` or a default value
if the variable is unset. If no `default` is provided, `nil` is returned.

> #### Untrusted Input {: .warning}
>
> This conversion routine uses `String.to_atom/1` and may result in atom exhaustion if
> used without the `:allowed` option. See [Preventing atom exhaustion](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/atom_exhaustion) from the Security Working
> Group of the Erlang Ecosystem Foundation.

### Options

- `:allowed`: A list of `t:atom/0` values indicating permitted atoms and used as
  a lookup table, if present. Any value not found will result in an exception.

- `:default`: The `t:atom/0` or `t:binary/0` value representing the atom value to use if
  the environment variable is unset (`nil`). If the `:allowed` option is present, the
  default value must be one of the permitted values.

- `:downcase`: See `t:Enviable.Conversion.opt_atom_downcase/0`.

- `:upcase`: See `t:Enviable.Conversion.opt_atom_upcase/0`.

A shorthand for the `default` option may be provided as a `t:atom/0` value.

### Examples

```elixir
iex> Enviable.get_env_as_atom("UNSET")
nil

iex> Enviable.get_env_as_atom("UNSET", :default)
:default

iex> Enviable.get_env_as_atom("UNSET", default: :default)
:default

iex> Enviable.put_env("NAME", "get_env_as_atom")
iex> Enviable.get_env_as_atom("NAME")
:get_env_as_atom
```

# `get_env_as_base16`
*since 1.4.0* 

```elixir
@spec get_env_as_base16(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:case, :upper | :lower | :mixed}
  | {atom(), term()}
]) :: nil | term()
```

Returns the value of an environment variable decoded from a base 16 string or `nil` if
the environment variable is not set.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:case`: The value of `:case` passed to `Base.decode16/2`, which must be `:upper`,
  `:lower`, or `:mixed`. The default value is `:mixed`.

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.encode16("RED", case: :lower))
iex> Enviable.get_env_as_base16("NAME")
"RED"

iex> Enviable.put_env("NAME", Base.encode16("RED", case: :lower))
iex> Enviable.get_env_as_base16("NAME", case: :lower)
"RED"

iex> Enviable.put_env("NAME", Base.encode16("RED", case: :lower))
iex> Enviable.get_env_as_base16("NAME", as: :string, case: :lower)
"RED"

iex> Enviable.put_env("NAME", Base.encode16("RED", case: :lower))
iex> Enviable.get_env_as_base16("NAME", as: :atom, case: :lower, downcase: true)
:red
```

# `get_env_as_base32`
*since 1.4.0* 

```elixir
@spec get_env_as_base32(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:case, :upper | :lower | :mixed}
  | {:padding, boolean()}
  | {atom(), term()}
]) :: nil | term()
```

Returns the value of an environment variable decoded from a base 32 string or `nil` if
the environment variable is not set.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:case`: The value of `:case` passed to `Base.decode32/2`, which must be `:upper`,
  `:lower`, or `:mixed`. The default is `:mixed`.

- `:padding`: The boolean value of `:padding` passed to `Base.decode32/2`. The default
  is `false` (the opposite of `Base.decode32/2`).

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.encode32("RED", case: :lower))
iex> Enviable.get_env_as_base32("NAME")
"RED"

iex> Enviable.put_env("NAME", Base.encode32("RED", case: :lower))
iex> Enviable.get_env_as_base32("NAME", case: :lower)
"RED"

iex> Enviable.put_env("NAME", Base.encode32("RED", case: :lower))
iex> Enviable.get_env_as_base32("NAME", as: :string, case: :lower)
"RED"

iex> Enviable.put_env("NAME", Base.encode32("RED", case: :lower))
iex> Enviable.get_env_as_base32("NAME", as: :atom, case: :lower, downcase: true)
:red
```

# `get_env_as_base64`
*since 1.4.0* 

```elixir
@spec get_env_as_base64(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:ignore_whitespace, boolean()}
  | {:padding, boolean()}
  | {atom(), term()}
]) :: nil | term()
```

Returns the value of an environment variable decoded from a base 64 string or `nil` if
the environment variable is not set.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:ignore_whitespace`: Whether to ignore whitespace values. The default is `true`,
  the opposite default for both `Base.decode64/2` and `Base.url_decode64/2`.

- `:padding`: The boolean value of `:padding` passed to `Base.decode64/2`. The default
  is `false` (the opposite of `Base.decode64/2`).

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.encode64("RED", padding: true))
iex> Enviable.get_env_as_base64("NAME")
"RED"

iex> Enviable.put_env("NAME", Base.encode64("RED", padding: true))
iex> Enviable.get_env_as_base64("NAME", padding: false)
"RED"

iex> Enviable.put_env("NAME", Base.encode64("RED", padding: true))
iex> Enviable.get_env_as_base64("NAME", as: :string, padding: true)
"RED"

iex> Enviable.put_env("NAME", Base.encode64("RED", padding: true))
iex> Enviable.get_env_as_base64("NAME", as: :atom, downcase: true, padding: false)
:red
```

# `get_env_as_boolean`
*since 1.3.0* 

```elixir
@spec get_env_as_boolean(
  String.t(),
  boolean()
  | [
      {:default, boolean()}
      | Enviable.Conversion.opt_bool_downcase()
      | {:truthy | :falsy, [binary()]}
    ]
) :: boolean()
```

Returns the value of an environment variable converted to a `t:boolean/0` value, or
a default value if the variable is unset. If no `default` is provided, `false` will be
returned.

This function will always result in a `t:boolean/0` value. Unless configured with
`truthy` or `falsy`, only the values `"1"` and `"true"` will be converted to `true` and
any other value will result in `false`.

### Options

- `:default`: the default value (which must be `true` or `false`) if the variable is
  unset. In most cases, when `falsy` is provided, `default: true` should also be
  provided.

- `:truthy`: a list of string values to be compared for truth values. If the value of
  the environment variable matches these values, `true` will be returned; other values
  will result in `false`. Mutually exclusive with `falsy`.

- `:falsy`: a list of string values to be compared for false values. If the value of the
  environment variable matches these values, `false` will be returned; other values will
  result in `true`. Mutually exclusive with `truthy`.

- `:downcase`: either `false`, `true`, or the mode parameter for `String.downcase/2`
  (`:default`, `:ascii`, `:greek`, or `:turkic`). The default is `:default`.

  The default `:downcase` value for boolean conversions can be changed at compile time
  through application configuration:

  ```elixir
  config :enviable, :boolean_downcase, true
  config :enviable, :boolean_downcase, :default
  config :enviable, :boolean_downcase, :ascii
  ```

A shorthand value for the `default` option may be provided as a `t:boolean/0` value.

### Examples

```elixir
iex> Enviable.get_env_as_boolean("FLAG")
false

iex> Enviable.get_env_as_boolean("FLAG", true)
true

iex> Enviable.get_env_as_boolean("FLAG", default: true)
true

iex> Enviable.put_env("FLAG", "1")
iex> Enviable.get_env_as_boolean("FLAG")
true

iex> Enviable.put_env("FLAG", "something")
iex> Enviable.get_env_as_boolean("FLAG")
false

iex> Enviable.put_env("FLAG", "oui")
iex> Enviable.get_env_as_boolean("FLAG", truthy: ["oui"])
true

iex> Enviable.put_env("FLAG", "OUI")
iex> Enviable.get_env_as_boolean("FLAG", truthy: ["oui"])
true
iex> Enviable.get_env_as_boolean("FLAG", truthy: ["oui"], downcase: false)
false

iex> Enviable.put_env("FLAG", "NON")
iex> Enviable.get_env_as_boolean("FLAG", falsy: ["non"])
false
iex> Enviable.get_env_as_boolean("FLAG", falsy: ["non"], downcase: false)
true

iex> Enviable.get_env_as_boolean("FLAG", default: nil)
** (ArgumentError) could not convert environment variable "FLAG" to type boolean: non-boolean `default` value

iex> Enviable.get_env_as_boolean("FLAG", downcase: nil)
** (ArgumentError) could not convert environment variable "FLAG" to type boolean: invalid `downcase` value

iex> Enviable.get_env_as_boolean("FLAG", truthy: ["oui"], falsy: ["non"])
** (ArgumentError) could not convert environment variable "FLAG" to type boolean: `truthy` and `falsy` options both provided
```

# `get_env_as_charlist`
*since 1.3.0* 

```elixir
@spec get_env_as_charlist(String.t(), binary() | [{:default, charlist() | binary()}]) ::
  nil | charlist()
```

Returns the value of an environment variable converted to a `t:charlist/0` or a default
value if the variable is unset. If no `default` is provided, `nil` is returned.

### Options

- `:default`: The default value, either as a `t:charlist/0` or `t:binary/0`.

A shorthand for the `default` value may be provided as a `t:binary/0` (there is no guard
for `t:charlist/0`).

### Examples

```elixir
iex> Enviable.get_env_as_charlist("UNDEFINED")
nil

iex> Enviable.get_env_as_charlist("UNDEFINED", "default")
~c"default"

iex> Enviable.get_env_as_charlist("UNDEFINED", default: "default")
~c"default"

iex> Enviable.get_env_as_charlist("UNDEFINED", default: ~c"default")
~c"default"

iex> Enviable.put_env("NAME", "get_env_as")
iex> Enviable.get_env_as_charlist("NAME")
~c"get_env_as"
```

# `get_env_as_decimal`
*since 1.6.0* 

```elixir
@spec get_env_as_decimal(
  String.t(),
  Decimal.t()
  | float()
  | integer()
  | binary()
  | [{:default, Decimal.t() | binary() | float() | integer()}]
) :: Decimal.t() | nil
```

Returns the value of an environment variable converted to a `t:Decimal.t/0` value or
a default value if the variable is unset. If no `default` is provided, `nil` will be
returned.

### Options

- `:default`: The default value, either as `t:Decimal.t/0`, `t:float/0`,
  `t:integer/0`, or `t:binary/0` (the latter three must convert cleanly to
  `t:Decimal.t/0`).

A shorthand for the `default` value may be provided as a `t:Decimal.t/0`, `t:float/0`,
`t:integer/0`, or `t:binary/0` value.

### Examples

```elixir
iex> Enviable.get_env_as_decimal("DECIMAL")
nil

iex> Enviable.get_env_as_decimal("DECIMAL", Decimal.new("3.14"))
Decimal.new("3.14")

iex> Enviable.get_env_as_decimal("DECIMAL", 25.5)
Decimal.new("25.5")

iex> Enviable.get_env_as_decimal("DECIMAL", 25)
Decimal.new("25")

iex> Enviable.get_env_as_decimal("DECIMAL", "255")
Decimal.new("255")

iex> Enviable.get_env_as_decimal("DECIMAL", default: Decimal.new("3.14"))
Decimal.new("3.14")

iex> Enviable.get_env_as_decimal("DECIMAL", default: 25.5)
Decimal.new("25.5")

iex> Enviable.get_env_as_decimal("DECIMAL", default: 25)
Decimal.new("25")

iex> Enviable.get_env_as_decimal("DECIMAL", default: "255")
Decimal.new("255")

iex> Enviable.get_env_as_decimal("DECIMAL", default: "3.5R")
** (ArgumentError) could not convert environment variable "DECIMAL" to type decimal: invalid decimal `default` value

iex> Enviable.get_env_as_decimal("DECIMAL", default: %{})
** (ArgumentError) could not convert environment variable "DECIMAL" to type decimal: invalid decimal `default` value

iex> Enviable.put_env("DECIMAL", "1")
iex> Enviable.get_env_as_decimal("DECIMAL")
Decimal.new("1")

iex> Enviable.put_env("DECIMAL", "ff")
iex> Enviable.get_env_as_decimal("DECIMAL")
** (Enviable.ConversionError) could not convert environment variable "DECIMAL" to type decimal
```

# `get_env_as_duration`
*since 2.2.0* 

```elixir
@spec get_env_as_duration(
  String.t(),
  String.t() | Duration.t() | [{:default, String.t() | Duration.t()}]
) :: Duration.t()
```

Returns the value of an environment variable converted to a `t:Duration.t/0` or
a default value if the variable is unset. If no `default` is provided, `nil` will be
returned.

### Timeout Values

Timeout values are specified as non-negative integer values with optional suffixes or
the word `infinity`. The integer part may have underscores (`_`) separating digits like
Elixir itself.

If no suffix is present, the value is in milliseconds. Supported suffixes are:

- `week`, `weeks`, `w`: the number of weeks (always 7 days)

- `day`, `days`, `d`: the number of days (always 24 hours)

- `hour`, `hours`, `h`: the number of hours (always 60 minutes)

- `minute`, `minutes`, `m`: the number of minutes (always 60 seconds)

- `second`, `seconds`, `s`: the number of seconds (always 1000 milliseconds)

- `millisecond`, `milliseconds`, `ms`: the number of milliseconds

Suffixes may be present with or without a space (`30s` and `30 s` are the same value),
and multiple timeouts may be chained (`1h 30m`), but may not be duplicated. See
`Kernel.to_timeout/1` for more details.

Only lowercase suffixes are supported.

### Options

- `:default`: The default value. a `t:Duration.t/0` struct or an ISO8601 duration
  string parseable by `Duration.from_iso8601/1`.

### Examples

```elixir
iex> Enviable.get_env_as_duration("UNSET")
nil

iex> Enviable.get_env_as_duration("UNSET", "PT30S")
%Duration{second: 30}

iex> Enviable.put_env("TIMEOUT", "PT1H30M")
iex> Enviable.get_env_as_duration("TIMEOUT")
%Duration{hour: 1, minute: 30}
```

# `get_env_as_elixir`
*since 1.3.0* 

```elixir
@spec get_env_as_elixir(String.t()) :: nil | term()
```

Returns the value of an environment variable parsed and evaluated as Elixir code, or
`nil` if the environment variable is not set.

This can be used for tuples, complex map declarations, or other expressions difficult to
represent with other types. Longer code blocks should be encoded as base 64 text and
decoded with `{:base64, :elixir}`.

> #### Untrusted Input {: .error}
>
> This function parses (with `Code.string_to_quoted/1`) and evaluates (with
> `Code.eval_quoted/1`) elixir code from environment variables in the context of your
> application. Do not use this with untrusted input.

### Examples

```elixir
iex> Enviable.get_env_as_elixir("TERM")
nil

iex> Enviable.put_env("TERM", "11000..11100//3")
iex> Enviable.get_env_as_elixir("TERM")
11000..11100//3
```

# `get_env_as_erlang`
*since 1.3.0* 

```elixir
@spec get_env_as_erlang(String.t()) :: nil | term()
```

Returns the value of an environment variable parsed and evaluated as Erlang code, or
`nil` if the environment variable is not set.

This can be used for tuples, complex map declarations, or other expressions difficult to
represent with other types. Longer code blocks should be encoded as base 64 text and
decoded with `{:base64, :erlang}`.

> #### Untrusted Input {: .error}
>
> This function parses (with `:erl_scan.string/1`) and evaluates (with
> `:erl_parse.parse_term/1`) Erlang code from environment variables in the context of
> your application. Do not use this with untrusted input.

### Examples

```elixir
iex> Enviable.get_env_as_erlang("TERM")
nil

iex> Enviable.put_env("TERM", "{ok, true}.")
iex> Enviable.get_env_as_erlang("TERM")
{:ok, true}
```

# `get_env_as_float`
*since 1.3.0* 

```elixir
@spec get_env_as_float(
  String.t(),
  float() | integer() | binary() | [{:default, binary() | float() | integer()}]
) :: float() | nil
```

Returns the value of an environment variable converted to a `t:float/0` value or
a default value if the variable is unset. If no `default` is provided, `nil` will be
returned.

### Options

- `:default`: The default value, either as `t:float/0`, `t:integer/0` (which will be
  converted to float), or `t:binary/0` (which must parse cleanly as float).

A shorthand for the `default` value may be provided as a `t:float/0`, `t:integer/0`, or
`t:binary/0` value.

### Examples

```elixir
iex> Enviable.get_env_as_float("FLOAT")
nil

iex> Enviable.get_env_as_float("FLOAT", 25.5)
25.5

iex> Enviable.get_env_as_float("FLOAT", 25)
25.0

iex> Enviable.get_env_as_float("FLOAT", "255")
255.0

iex> Enviable.get_env_as_float("FLOAT", default: 25.5)
25.5

iex> Enviable.get_env_as_float("FLOAT", default: 25)
25.0

iex> Enviable.get_env_as_float("FLOAT", default: "255")
255.0

iex> Enviable.get_env_as_float("FLOAT", default: "3.5R")
** (ArgumentError) could not convert environment variable "FLOAT" to type float: non-float `default` value

iex> Enviable.put_env("FLOAT", "1")
iex> Enviable.get_env_as_float("FLOAT")
1.0

iex> Enviable.put_env("FLOAT", "ff")
iex> Enviable.get_env_as_float("FLOAT")
** (Enviable.ConversionError) could not convert environment variable "FLOAT" to type float
```

# `get_env_as_hex32`
*since 1.4.0* 

```elixir
@spec get_env_as_hex32(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:case, :upper | :lower | :mixed}
  | {:padding, boolean()}
  | {atom(), term()}
]) :: nil | term()
```

Returns the value of an environment variable decoded from a base 32 hex encoded string
or `nil` if the environment variable is not set.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:case`: The value of `:case` passed to `Base.hex_decode32/2`, which must be `:upper`,
  `:lower`, or `:mixed`. The default is `:mixed`.

- `:padding`: The boolean value of `:padding` passed to `hex.decode32/2`. The default is
  `false` (the opposite of `Base.hex_decode32/2`).

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.hex_encode32("RED", case: :lower))
iex> Enviable.get_env_as_hex32("NAME")
"RED"

iex> Enviable.put_env("NAME", Base.hex_encode32("RED", case: :lower))
iex> Enviable.get_env_as_hex32("NAME", case: :lower)
"RED"

iex> Enviable.put_env("NAME", Base.hex_encode32("RED", case: :lower))
iex> Enviable.get_env_as_hex32("NAME", as: :string, case: :lower)
"RED"

iex> Enviable.put_env("NAME", Base.hex_encode32("RED", case: :lower))
iex> Enviable.get_env_as_hex32("NAME", as: :atom, case: :lower, downcase: true)
:red
```

# `get_env_as_integer`
*since 1.3.0* 

```elixir
@spec get_env_as_integer(
  String.t(),
  integer() | binary() | [base: 2..36, default: binary() | integer()]
) :: integer() | nil
```

Returns the value of an environment variable converted to a `t:integer/0` value or
a default value if the variable is unset. If no `default` is provided, `nil` is
returned.

### Options

- `:base`: The base (`2..36`) for integer conversion. Defaults to base `10` like
  `String.to_integer/2`.

- `:default`: the default value, which must be either a binary string value or an
  integer. If provided as a binary, this will be interpreted according to the `base`
  option provided.

A shorthand for the `default` value may be provided as a `t:integer/0` or `t:binary/0`
value.

Failure to parse a binary string `default` or the value of the environment variable will
result in an exception.

### Examples

```elixir
iex> Enviable.get_env_as_integer("PORT")
nil

iex> Enviable.get_env_as_integer("PORT", 5432)
5432

iex> Enviable.get_env_as_integer("PORT", "5432")
5432

iex> Enviable.get_env_as_integer("PORT", default: 5432)
5432

iex> Enviable.get_env_as_integer("PORT", default: "5432")
5432

iex> Enviable.get_env_as_integer("PORT", default: 3.5)
** (ArgumentError) could not convert environment variable "PORT" to type integer: non-integer `default` value

iex> Enviable.put_env("PORT", "5432")
iex> Enviable.get_env_as_integer("PORT")
5432

iex> Enviable.put_env("PORT", "18eb")
iex> Enviable.get_env_as_integer("PORT")
** (Enviable.ConversionError) could not convert environment variable "PORT" to type integer

iex> Enviable.put_env("PORT", "18EB")
iex> Enviable.get_env_as_integer("PORT", base: 16)
6379
```

# `get_env_as_json`
*since 1.3.0* 

```elixir
@spec get_env_as_json(String.t(),
  default: Enviable.Conversion.json(),
  engine: module() | (String.t() -&gt; Enviable.Conversion.json())
) :: Enviable.Conversion.json() | nil
```

Returns the value of an environment variable converted to
a `t:Enviable.Conversion.json/0` value or a default value if the variable is unset. If
no `default` is provided, `nil` will be returned.

### Options

- `:default`: The default value, which may be any valid JSON type.

- `:engine`: The JSON engine to use. May be provided as a `t:module/0` (which must
  export `decode/1`), an arity 1 function, or a `t:mfa/0` tuple. When provided with
  a `t:mfa/0`, the variable value will be passed as the first parameter.

  If the engine produces `{:ok, json_value}` or an expected JSON type result, it will be
  considered successful. Any other result will be treated as failure.

  The default JSON engine is `JSON` if Elixir's `m:JSON` is available, `:json` if the
  Erlang/OTP `m::json` module is available (Erlang/OTP 27+) or provided by
  [json_polyfill][jp]. Otherwise, [Jason][jason] is the default engine. This choice may
  be overridden with application configuration, as this example shows using
  [Thoas][thoas].

  ```elixir
  import Config

  config :enviable, :json_engine, :thoas
  ```

[jp]: https://hexdocs.pm/json_polyfill/readme.html
[jason]: https://hexdocs.pm/jason/readme.html
[thoas]: https://hexdocs.pm/thoas/readme.html

### Examples

```elixir
iex> Enviable.get_env_as_json("JSON")
nil

iex> Enviable.get_env_as_json("JSON", default: "3.5R")
"3.5R"

iex> Enviable.put_env("JSON", ~S|[{"foo":"bar"}]|)
iex> Enviable.get_env_as_json("JSON", engine: &Jason.decode!/1)
[%{"foo" => "bar"}]

iex> Enviable.put_env("JSON", ~S|[{"foo":"bar"}]|)
iex> Enviable.get_env_as_json("JSON")
[%{"foo" => "bar"}]

iex> Enviable.put_env("JSON", "ff")
iex> Enviable.get_env_as_json("JSON")
** (Enviable.ConversionError) could not convert environment variable "JSON" to type json

iex> Enviable.put_env("JSON", "ff")
iex> Enviable.get_env_as_json("JSON", engine: &Jason.decode!/1)
** (Enviable.ConversionError) could not convert environment variable "JSON" to type json
```

# `get_env_as_list`
*since 1.4.0* 

```elixir
@spec get_env_as_list(String.t(), [{:default, term()} | as_list_opt()]) ::
  nil | list()
```

Returns the value of an environment variable parsed as a delimiter-separated list.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Each entry must conform to the permitted
  type provided in `:as`.

- `:delimiter`: The delimiter used to separate the list. This must be a pattern accepted
  by `String.split/3` (a string, a list of strings, a compiled binary pattern, or
  a regular expression). Defaults to `","`.

- `:parts`: The maximum number of parts to split into (`t:pos_integer/0` or
  `:infinity`). Passed to `String.split/3`.

- `:trim`: A boolean option whether empty entries should be omitted.

When the pattern is a regular expression, `Regex.split/3` options are also supported:

- `:on`: specifies which captures to split the string on, and in what order. Defaults to
  :first which means captures inside the regex do not affect the splitting process.

- `:include_captures`: when true, includes in the result the matches of the regular
  expression. The matches are not counted towards the maximum number of parts if
  combined with the `:parts` option. Defaults to `false`.

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("LIST", "1,2,3")
iex> Enviable.get_env_as_list("LIST")
["1", "2", "3"]
iex> Enviable.get_env_as_list("LIST", as: :integer)
[1, 2, 3]

iex> Enviable.put_env("LIST", "1;2;3")
iex> Enviable.get_env_as_list("LIST", delimiter: ";")
["1", "2", "3"]
iex> Enviable.get_env_as_list("LIST", as: :integer, delimiter: ";")
[1, 2, 3]
```

# `get_env_as_log_level`
*since 1.3.0* 

```elixir
@spec get_env_as_log_level(String.t(), [{:default, Enviable.Conversion.log_level()}]) ::
  nil | Enviable.Conversion.log_level()
```

Returns the value of an environment variable converted to a log level `t:atom/0` for
`Logger.configure/1` or a default value if the variable is unset. If no `default` is
provided, `nil` will be returned.

### Options

- `:default`: The default value. Must be a valid value.

A shorthand for the `default` option may be provided as a `t:atom/0` or `t:binary/0`
value.

### Examples

```elixir
iex> Enviable.get_env_as_log_level("LOG_LEVEL")
nil

iex> Enviable.get_env_as_log_level("LOG_LEVEL", :error)
:error

iex> Enviable.get_env_as_log_level("LOG_LEVEL", "info")
:info

iex> Enviable.get_env_as_log_level("LOG_LEVEL", default: :error)
:error

iex> Enviable.get_env_as_log_level("LOG_LEVEL", default: "info")
:info

iex> Enviable.put_env("LOG_LEVEL", "critical")
iex> Enviable.get_env_as_log_level("LOG_LEVEL")
:critical
```

# `get_env_as_module`
*since 1.3.0* 

```elixir
@spec get_env_as_module(
  String.t(),
  module() | binary() | [allowed: [module()], default: module() | binary()]
) :: nil | module()
```

Returns the value of an environment variable converted to `t:module/0` or a default
value if the variable is unset. If no `default` is provided, `nil` will be returned.

> #### Untrusted Input {: .warning}
>
> This conversion routine uses `Module.concat/1` and may result in atom exhaustion if
> used without the `:allowed` option. See [Preventing atom exhaustion](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/atom_exhaustion) from the Security Working
> Group of the Erlang Ecosystem Foundation.

### Options

- `:allowed`: A list of `t:module/0` values indicating permitted module and used as
  a lookup table, if present. Any value not found will result in an exception.

- `:default`: The `t:module/0` or `t:binary/0` value representing the atom value to use
  if the environment variable is unset (`nil`). If the `:allowed` option is present, the
  default value must be one of the permitted values.

A shorthand for the `default` option may be provided as a `t:atom/0` or `t:binary/0`
value.

### Examples

```elixir
iex> Enviable.get_env_as_module("UNSET")
nil

iex> Enviable.get_env_as_module("UNSET", Enviable)
Elixir.Enviable

iex> Enviable.put_env("NAME", "Enviable")
iex> Enviable.get_env_as_module("NAME")
Elixir.Enviable
```

# `get_env_as_pem`
*since 1.3.0* 

```elixir
@spec get_env_as_pem(String.t(), [{:filter, boolean() | :cert | :key}]) ::
  nil | Enviable.Conversion.pem()
```

Returns the value of an environment variable converted from a PEM string through
`:public_key.pem_decode/1` or `nil` if the variable is unset.

### Options

- `:filter`: Filters the output of `:public_key.pem_decode/1`. Permitted values are
  `false`, `true`, `:cert`, or `:key`. The default is `true`.

  - `false`: the list is returned without processing, suitable for further processing
    with `:public_key.pem_entry_decode/2`.

  - `true`: returns the first unencrypted `:PrivateKeyInfo` found, a list of unencrypted
    `:Certificate` records, or an empty list.

  - `:cert`: returns a list of unencrypted `:Certificate` records or raises an
    exception if none are found.

  - `:key`: returns the first unencrypted `:PrivateKeyIinfo` record or raises an
    exception if one is not found.

### Examples

```elixir
iex> Enviable.get_env_as_pem("PEM")
nil

iex> Enviable.put_env("PEM", "")
iex> Enviable.get_env_as_pem("PEM")
[]
```

# `get_env_as_safe_atom`
*since 1.3.0* 

```elixir
@spec get_env_as_safe_atom(
  String.t(),
  atom()
  | binary()
  | [
      {:allowed, [atom()]}
      | {:default, atom() | binary()}
      | Enviable.Conversion.opt_atom_downcase()
    ]
) :: nil | atom()
```

Returns the value of an environment variable converted to an existing `t:atom/0` or
a default value if the variable is unset. If no `default` is provided, `nil` is
returned.

> #### Untrusted Input {: .warning}
>
> This conversion routine uses `String.to_existing_atom/1` which will result in an
> exception if the resulting atom is not already known and if used without the
> `:allowed` option. See [Preventing atom exhaustion](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/atom_exhaustion) from the Security Working Group of the
> Erlang Ecosystem Foundation.

### Options

- `:allowed`: A list of `t:atom/0` values indicating permitted atoms and used as
  a lookup table, if present. Any value not found will result in an exception.

- `:default`: The `t:atom/0` or `t:binary/0` value representing the atom value to use if
  the environment variable is unset (`nil`). If the `:allowed` option is present, the
  default value must be one of the permitted values.

- `:downcase`: See `t:Enviable.Conversion.opt_atom_downcase/0`.

- `:upcase`: See `t:Enviable.Conversion.opt_atom_upcase/0`.

A shorthand for the `default` option may be provided as a `t:atom/0` value.

### Examples

```elixir
iex> Enviable.get_env_as_safe_atom("UNSET")
nil

iex> Enviable.get_env_as_safe_atom("UNSET", :default)
:default

iex> Enviable.get_env_as_safe_atom("UNSET", default: :default)
:default

iex> Enviable.put_env("NAME", "GET_ENV_AS_SAFE_ATOM")
iex> Enviable.get_env_as_safe_atom("NAME", downcase: true)
:get_env_as_safe_atom
```

# `get_env_as_safe_module`
*since 1.3.0* 

```elixir
@spec get_env_as_safe_module(
  String.t(),
  binary() | module() | [allowed: [module()], default: module() | binary()]
) :: nil | module()
```

Returns the value of an environment variable converted to `t:module/0` or a default
value if the variable is unset. If no `default` is provided, `nil` will be returned. The
resulting `t:module/0` must already exist.

> #### Untrusted Input {: .warning}
>
> This conversion routine uses `Module.safe_concat/1` which will result in an exception
> if the resulting module is not already known and if used without the `:allowed`
> option. See [Preventing atom exhaustion](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/atom_exhaustion) from the Security Working Group of the Erlang Ecosystem
> Foundation.

### Options

- `:allowed`: A list of `t:module/0` values indicating permitted module and used as
  a lookup table, if present. Any value not found will result in an exception.

- `:default`: The `t:module/0` or `t:binary/0` value representing the atom value to use
  if the environment variable is unset (`nil`). If the `:allowed` option is present, the
  default value must be one of the permitted values.

A shorthand for the `default` option may be provided as a `t:atom/0` or `t:binary/0`
value.

### Examples

```elixir
iex> Enviable.get_env_as_safe_module("UNSET")
nil

iex> Enviable.get_env_as_safe_module("UNSET", Enviable)
Elixir.Enviable

iex> Enviable.put_env("NAME", "Enviable")
iex> Enviable.get_env_as_safe_module("NAME")
Elixir.Enviable
```

# `get_env_as_timeout`
*since 1.7.0* 

```elixir
@spec get_env_as_timeout(
  String.t(),
  Enviable.Conversion.timeout_default()
  | [{:default, Enviable.Conversion.timeout_default()}]
) :: timeout()
```

Returns the value of an environment variable converted to a millisecond `t:timeout/0` or
a default value if the variable is unset. If no `default` is provided, `:infinity` will
be returned.

### Timeout Values

Timeout values are specified as non-negative integer values with optional suffixes or
the word `infinity`. The integer part may have underscores (`_`) separating digits like
Elixir itself.

If no suffix is present, the value is in milliseconds. Supported suffixes are:

- `week`, `weeks`, `w`: the number of weeks (always 7 days)

- `day`, `days`, `d`: the number of days (always 24 hours)

- `hour`, `hours`, `h`: the number of hours (always 60 minutes)

- `minute`, `minutes`, `m`: the number of minutes (always 60 seconds)

- `second`, `seconds`, `s`: the number of seconds (always 1000 milliseconds)

- `millisecond`, `milliseconds`, `ms`: the number of milliseconds

Suffixes may be present with or without a space (`30s` and `30 s` are the same value),
and multiple timeouts may be chained (`1h 30m`), but may not be duplicated. See
`Kernel.to_timeout/1` for more details.

Only lowercase suffixes are supported.

### Options

- `:default`: The default value. This may be a timeout value described above,
  a `t:Duration.t/0`, a `t:timeout/0` value, or a `t:keyword/0` list where the keys may
  be `:week`, `:day`, `:hour`, `:minute`, `:second`, or `:millisecond` as described in
  `Kernel.to_timeout/1`.

### Examples

```elixir
iex> Enviable.get_env_as_timeout("UNSET")
:infinity

iex> Enviable.get_env_as_timeout("UNSET", "30s")
30000

iex> Enviable.get_env_as_timeout("UNSET", "PT30S")
30000

iex> Enviable.put_env("TIMEOUT", "3_0 seconds")
iex> Enviable.get_env_as_timeout("TIMEOUT")
30000

iex> Enviable.put_env("TIMEOUT", "PT30S")
iex> Enviable.get_env_as_timeout("TIMEOUT")
30000
```

# `get_env_as_url_base64`
*since 1.4.0* 

```elixir
@spec get_env_as_url_base64(String.t(), [
  {:default, term()}
  | {:as, :string | Enviable.Conversion.primitive()}
  | {:ignore_whitespace, boolean()}
  | {:padding, boolean()}
  | {atom(), term()}
]) :: nil | term()
```

Returns the value of an environment variable decoded from a URL-safe base 64 string or
`nil` if the environment variable is not set.

## Options

- `:as`: The type of value that the encoded string is to be parsed as once decoded. Must
  be either `:string` (the same as not providing `as: :string`) or
  a `t:Conversion.primitive/0` value.

- `:default`: The default value to be used. Must conform to the permitted type provided
  in `:as`.

- `:ignore_whitespace`: Whether to ignore whitespace values. The default is `true`,
  the opposite default for both `Base.decode64/2` and `Base.url_decode64/2`.

- `:padding`: The boolean value of `:padding` passed to `Base.decode64/2`. The default
  is `false` (the opposite of `Base.decode64/2`).

If `:as` is provided, the options for that type may also be provided.

### Examples

```elixir
iex> Enviable.put_env("NAME", Base.url_encode64("RED", padding: true))
iex> Enviable.get_env_as_url_base64("NAME")
"RED"

iex> Enviable.put_env("NAME", Base.url_encode64("RED", padding: true))
iex> Enviable.get_env_as_url_base64("NAME", padding: false)
"RED"

iex> Enviable.put_env("NAME", Base.url_encode64("RED", padding: true))
iex> Enviable.get_env_as_url_base64("NAME", as: :string, padding: true)
"RED"

iex> Enviable.put_env("NAME", Base.url_encode64("RED", padding: true))
iex> Enviable.get_env_as_url_base64("NAME", as: :atom, downcase: true, padding: false)
:red
```

# `delete_env`

```elixir
@spec delete_env(String.t()) :: :ok
```

Deletes an environment variable, removing `varname` from the environment.

# `fetch_env`

```elixir
@spec fetch_env(String.t()) :: {:ok, String.t()} | :error
```

Returns the value of the given environment variable or `:error` if not found.

If the environment variable `varname` is set, then `{:ok, value}` is returned where
value is a string. If `varname` is not set, `:error` is returned.

### Examples

```elixir
iex> Enviable.fetch_env("PORT")
:error

iex> Enviable.put_env("PORT", "4000")
iex> Enviable.fetch_env("PORT")
{:ok, "4000"}
```

# `fetch_env!`

```elixir
@spec fetch_env!(String.t()) :: String.t()
```

Returns the value of the given environment variable or raises if not found.

Same as `get_env/1` but raises instead of returning `nil` when the variable is not set.

### Examples

```elixir
iex> Enviable.fetch_env!("UNSET")
** (System.EnvError) could not fetch environment variable "UNSET" because it is not set

iex> Enviable.put_env("PORT", "4000")
iex> Enviable.fetch_env!("PORT")
"4000"
```

# `get_env`

```elixir
@spec get_env() :: %{optional(String.t()) =&gt; String.t()}
```

Returns all system environment variables.

The returned value is a map containing name-value pairs. Variable names and their values
are strings.

# `get_env`

```elixir
@spec get_env(String.t(), default) :: String.t() | default
when default: String.t() | nil
```

Returns the value of the given environment variable.

The returned value of the environment variable `varname` is a string. If the environment
variable is not set, returns the string specified in `default` or `nil` if none is
specified.

### Examples

```elixir
iex> Enviable.get_env("PORT")
nil

iex> Enviable.get_env("PORT", "4001")
"4001"

iex> Enviable.put_env("PORT", "4000")
iex> Enviable.get_env("PORT")
"4000"
iex> Enviable.get_env("PORT", "4001")
"4000"
```

# `put_env`

```elixir
@spec put_env(Enumerable.t()) :: :ok
```

Sets multiple environment variables.

Sets a new value for each environment variable corresponding to each `{key, value}` pair
in `enum`. Keys and non-nil values are automatically converted to charlists. `nil`
values erase the given keys.

Overall, this is a convenience wrapper around `put_env/2` and `delete_env/2` with
support for different key and value formats.

# `put_env`

```elixir
@spec put_env(String.t(), String.t()) :: :ok
```

Sets an environment variable value.

Sets a new `value` for the environment variable `varname`.

---

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