Packages

A library for declaratively reading, validating, and casting environment variables into their proper Elixir types in runtime.exs.

Current section

Files

Jump to
env_guard lib macro.ex
Raw

lib/macro.ex

defmodule EnvGuard.Macros do
@moduledoc """
Macros for declaring environment variables in `runtime.exs`.
Import these into your config to fetch, cast, and validate environment
variables in a single expression:
import Config
require EnvGuard.Macros
import EnvGuard.Macros
if config_env() == :prod do
secret_key_base = required("SECRET_KEY_BASE", :string, min_length: 64)
phx_server = optional("PHX_SERVER", :boolean, false)
end
See `EnvGuard.Types` for the supported types and constraints.
"""
@doc """
Fetches a required environment variable, casting it to `type` and checking
`constraints`.
Returns the cast value. Raises a `RuntimeError` if the variable is not set,
cannot be cast to `type`, or violates one of the `constraints`.
## Examples
secret_key_base = required("SECRET_KEY_BASE", :string, min_length: 64)
pool_size = required("POOL_SIZE", :integer, min: 1, max: 100)
"""
defmacro required(env_name, type, constraints \\ []) do
quote do
case EnvGuard.test_var(unquote(env_name), unquote(type), unquote(constraints)) do
{:ok, value} ->
value
{:error, :env_var_not_set} ->
raise "Environment variable #{unquote(env_name)} is not set"
{:error, :invalid_type, err} ->
raise "Environment variable #{unquote(env_name)} is not of type #{inspect(unquote(type))}. #{err}"
{:error, :constraint_violation, err} ->
raise "Environment variable #{unquote(env_name)} does not meet constraints. #{err}"
end
end
end
@doc """
Fetches an optional environment variable, falling back to `default`.
When the variable is set, it is cast to `type` and checked against
`constraints`, and the cast value is returned. When it is not set — or it is
set but fails casting or a constraint — `default` is returned instead. In the
failure cases a warning is logged via `Logger` before falling back.
## Examples
phx_server = optional("PHX_SERVER", :boolean, false)
log_level = optional("LOG_LEVEL", {:enum, ["debug", "info", "warning"]}, "info")
"""
defmacro optional(env_name, type, default, constraints \\ []) do
quote do
case EnvGuard.test_var(unquote(env_name), unquote(type), unquote(constraints)) do
{:ok, value} ->
value
{:error, :env_var_not_set} ->
unquote(default)
{:error, :invalid_type, err} ->
require Logger
Logger.warning(
"Environment variable #{unquote(env_name)} is set but not of type #{inspect(unquote(type))}. #{err} Falling back to default."
)
unquote(default)
{:error, :constraint_violation, err} ->
require Logger
Logger.warning(
"Environment variable #{unquote(env_name)} is set but does not meet constraints. #{err} Falling back to default."
)
unquote(default)
end
end
end
end