Packages

Read Unix env vars into application config

Current section

Files

Jump to
stillir src stillir.erl
Raw

src/stillir.erl

-module(stillir).
-export([set_config/2,
set_config/3,
set_config/4,
get_config/2,
get_config/3,
update_env/3]).
-type app_name() :: atom().
-type app_key() :: atom().
-type env_key() :: string().
-type env_var_value() :: string().
-type app_key_value() :: any().
-type default_value() :: app_key_value().
-type transform_fun() :: fun(((env_var_value())) -> app_key_value()).
-type default_fun() :: fun(() -> default_value()).
-type transform() :: integer|float|binary|atom|transform_fun().
-type opt() :: {default, any()|default_fun()}|{transform, transform()}|required.
-type opts() :: [opt()]|[].
-type config_spec() :: {app_key(), env_key()}|
{app_key(), env_key(), opts()}.
-type config_specs() :: [config_spec()].
-spec set_config(app_name(), config_specs()|[]) -> ok|no_return().
set_config(_, []) ->
ok;
set_config(AppName, [{AppKey, EnvKey}|Rest]) ->
set_config(AppName, AppKey, EnvKey),
set_config(AppName, Rest);
set_config(AppName, [{AppKey, EnvKey, Opts}|Rest]) ->
set_config(AppName, AppKey, EnvKey, Opts),
set_config(AppName, Rest).
-spec set_config(app_name(), app_key(), env_key()) -> ok|no_return().
set_config(AppName, AppKey, EnvKey) ->
set_config(AppName, AppKey, EnvKey, []).
-spec set_config(app_name(), app_key(), env_key(),
opts()) -> ok|no_return().
set_config(AppName, AppKey, EnvKey, Opts) ->
EnvValue = get_env(EnvKey),
set_env_value(AppName, AppKey, EnvKey, EnvValue, Opts).
-spec get_config(app_name(), app_key()) -> app_key_value()|no_return().
get_config(AppName, AppKey) ->
case application:get_env(AppName, AppKey) of
undefined ->
erlang:error({missing_config, AppKey});
{ok, Val} ->
Val
end.
-spec get_config(app_name(), app_key(), default_value()) -> app_key_value().
get_config(AppName, AppKey, DefaultValue) ->
case application:get_env(AppName, AppKey) of
undefined ->
DefaultValue;
{ok, Val} ->
Val
end.
-spec update_env(app_name(), file:filename_all(),
config_specs()|[]) -> ok|no_return().
update_env(AppName, Filename, Specs) ->
case file:open(Filename, [read, raw]) of
{ok, IoDev} ->
NewValues = read_file(IoDev, []),
reread_environment(AppName, NewValues, Specs);
{error, _Error} = Error ->
Error
end.
%% Internal
transform_value(Value, undefined) when is_list(Value) ->
Value;
transform_value(Value, integer) when is_list(Value) ->
list_to_integer(Value);
transform_value(Value, float) when is_list(Value) ->
list_to_float(Value);
transform_value(Value, binary) when is_list(Value) ->
list_to_binary(Value);
transform_value(Value, atom) when is_list(Value) ->
list_to_atom(Value);
transform_value(Value, Fun) when is_function(Fun, 1) andalso is_list(Value) ->
Fun(Value);
transform_value(Value, _) ->
Value.
reread_environment(_, _, []) ->
ok;
reread_environment(AppName, NewValues, [{AppKey, EnvKey}|Rest]) ->
set_config(AppName, AppKey, {EnvKey, NewValues}),
reread_environment(AppName, NewValues, Rest);
reread_environment(AppName, NewValues, [{AppKey, EnvKey, Opts}|Rest]) ->
set_config(AppName, AppKey, {EnvKey, NewValues}, Opts),
reread_environment(AppName, NewValues, Rest).
get_env({EnvKey, EnvList}) ->
case proplists:get_value(EnvKey, EnvList) of
undefined ->
missing_env_key;
EnvValue ->
{value, EnvValue}
end;
get_env(EnvKey) ->
case os:getenv(EnvKey) of
false ->
missing_env_key;
EnvValue ->
{value, EnvValue}
end.
get_default(Fun) when is_function(Fun, 0) ->
Fun();
get_default(Other) ->
Other.
set_env_value(AppName, AppKey, EnvKey, missing_env_key, Opts) ->
case proplists:is_defined(default, Opts) of
true ->
DefaultValue = get_default(proplists:get_value(default, Opts)),
set_env_value(AppName, AppKey, EnvKey, {value, DefaultValue}, Opts);
false ->
case proplists:get_value(required, Opts) of
true ->
erlang:error({missing_env_key, {AppName, EnvKey}});
_ ->
ok
end
end;
set_env_value(AppName, AppKey, _, {value, EnvValue}, Opts) ->
Transform = proplists:get_value(transform, Opts),
TransformedValue = transform_value(EnvValue, Transform),
set_env(AppName, AppKey, TransformedValue).
set_env(AppName, AppKey, Value) ->
application:set_env(AppName, AppKey, Value).
read_file(IoDev, Retval) ->
case file:read_line(IoDev) of
{ok, Data} ->
Res = handle_line(Data),
read_file(IoDev, Retval ++ [Res]);
eof ->
Retval;
{error, _Error} = Error ->
Error
end.
handle_line(Data) ->
case re:split(Data, "^export\s+([A-Z0-9_]+)='?([^']*)'?\n$") of
[_, EnvKey, EnvVar, _] ->
{binary_to_list(EnvKey), binary_to_list(EnvVar)};
Error ->
error_logger:info_msg("app=stillir at=handle_line error=~p", [Error]),
no_match
end.