%% @see amoc_config %% @copyright 2023 Erlang Solutions Ltd. %% @doc This module defines a behaviour to parse values as extracted from environment variables. %% %% The default implementation is `amoc_config_parser', which implements Erlang parsing. %% This way plain strings in valid Erlang syntax can be passed by env-vars %% and transformed into full Erlang terms. %% %% This module is to be used directly only for the read-only env init parameters, %% do not use it for the scenarios/helpers configuration, the amoc_config module %% must be used instead! This allows to provide configuration via REST API in a JSON format %% @end -module(amoc_config_env). -export([get/2]). -define(DEFAULT_PARSER_MODULE, amoc_config_parser). -callback(parse_value(string()) -> {ok, amoc_config:value()} | {error, any()}). %% ------------------------------------------------------------------ %% API %% ------------------------------------------------------------------ -spec get(amoc_config:name(), amoc_config:value()) -> amoc_config:value(). get(Name, Default) when is_atom(Name) -> EnvName = os_env_name(Name), Value = os:getenv(EnvName), case parse_value(Value, Default) of {ok, Term} -> Term; {error, Error} -> amoc_telemetry:execute_log( error, [config, env], #{error => Error, variable_name => EnvName, variable_value => Value, default_value => Default}, <<"cannot parse environment variable, using default value">>), Default end. %% ------------------------------------------------------------------ %% Internal Function Definitions %% ------------------------------------------------------------------ -spec os_env_name(amoc_config:name()) -> string(). os_env_name(Name) when is_atom(Name) -> "AMOC_" ++ string:uppercase(erlang:atom_to_list(Name)). -spec parse_value(string() | false, any()) -> {ok, amoc_config:value()} | {error, any()}. parse_value(false, Default) -> {ok, Default}; parse_value("", Default) -> {ok, Default}; parse_value(String, _) -> Mod = application:get_env(amoc, config_parser_mod, ?DEFAULT_PARSER_MODULE), try Mod:parse_value(String) of {ok, Value} -> {ok, Value}; {error, Error} -> {error, Error}; InvalidRetValue -> {error, {parser_returned_invalid_value, InvalidRetValue}} catch Class:Error:Stacktrace -> {error, {parser_crashed, {Class, Error, Stacktrace}}} end.