Current section
Files
Jump to
Current section
Files
lib/skogsra/system.ex
defmodule Skogsra.System do
@moduledoc """
This module defines the functions to get environment variables from the OS.
"""
alias Skogsra.Env
require Logger
############
# Public API
@doc """
Gets a OS environment variable value given a `Skogsra.Env` struct.
"""
@spec get_env(Env.t()) :: nil | term()
def get_env(env)
def get_env(%Env{options: options} = env) do
if options[:skip_system], do: nil, else: do_get_env(env)
end
#########
# Helpers
@doc false
@spec do_get_env(Env.t()) :: nil | term()
def do_get_env(env)
def do_get_env(%Env{} = env) do
module = Application.get_env(:skogsra, :system_module, System)
name = gen_env_name(env)
value = apply(module, :get_env, [name])
cast(env, name, value)
end
@doc false
@spec gen_env_name(Env.t()) :: binary()
def gen_env_name(env)
def gen_env_name(%Env{options: options} = env) do
case options[:os_env] do
nil ->
namespace = gen_namespace(env)
app_name = gen_app_name(env)
keys = gen_keys(env)
"#{namespace}#{app_name}_#{keys}"
value ->
value
end
end
@doc false
@spec gen_app_name(Env.t()) :: binary()
def gen_app_name(env)
def gen_app_name(%Env{app_name: app_name}) do
app_name
|> Atom.to_string()
|> String.upcase()
end
@doc false
@spec gen_keys(Env.t()) :: binary()
def gen_keys(env)
def gen_keys(%Env{keys: keys}) do
keys
|> Stream.map(&Atom.to_string/1)
|> Stream.map(&String.upcase/1)
|> Enum.join("_")
end
@doc false
@spec gen_namespace(Env.t()) :: binary()
def gen_namespace(env)
def gen_namespace(%Env{namespace: nil}) do
""
end
def gen_namespace(%Env{namespace: namespace}) do
value =
namespace
|> Module.split()
|> Stream.map(&String.upcase/1)
|> Enum.join("_")
"#{value}_"
end
@doc false
@spec cast(Env.t(), binary(), binary()) :: nil | term()
def cast(env, name, value)
def cast(_env, _name, nil) do
nil
end
def cast(%Env{} = env, name, value) do
type = get_type(env)
do_cast(type, name, value)
end
@doc false
@spec get_type(Env.t()) :: Env.type()
def get_type(env)
def get_type(%Env{options: options}) do
case options[:type] do
nil ->
type?(options[:default]) || :binary
type ->
type
end
end
@doc false
@spec type?(term()) :: nil | Env.type()
def type?(value)
def type?(nil), do: nil
def type?(value) when is_binary(value), do: :binary
def type?(value) when is_integer(value), do: :integer
def type?(value) when is_float(value), do: :float
def type?(value) when is_boolean(value), do: :boolean
def type?(value) when is_atom(value), do: :atom
def type?(_), do: nil
@doc false
@spec do_cast(Env.type(), binary(), binary()) :: nil | term()
def do_cast(type, name, value)
def do_cast(:binary, _name, value) do
value
end
def do_cast(:integer, name, value) do
case Integer.parse(value) do
{value, ""} ->
value
_ ->
fail_cast(:integer, name)
end
end
def do_cast(:float, name, value) do
case Float.parse(value) do
{value, ""} ->
value
_ ->
fail_cast(:float, name)
end
end
def do_cast(:boolean, name, value) do
case String.downcase(value) do
"true" ->
true
"false" ->
false
_ ->
fail_cast(:boolean, name)
end
end
def do_cast(:atom, _name, value) do
String.to_atom(value)
end
def do_cast({module, function} = pair, name, value) do
with {:ok, new_value} <- apply(module, function, [value]) do
new_value
else
{:error, reason} ->
fail_cast(pair, name, reason)
end
end
@doc false
@spec fail_cast(Env.type(), binary()) :: nil
@spec fail_cast(Env.type(), binary(), term()) :: nil
def fail_cast(type, name, reason \\ nil)
def fail_cast(type, name, nil) do
Logger.warn(fn ->
"OS variable #{name} cannot be casted to #{inspect(type)}"
end)
nil
end
def fail_cast(type, name, reason) do
Logger.warn(fn ->
"OS variable #{name} cannot be casted to #{inspect(type)} " <>
"due to #{inspect(reason)}"
end)
nil
end
end