Packages

Simple Erlang configuration handling library.

Current section

Files

Jump to
lib_conf src lib_conf.erl
Raw

src/lib_conf.erl

%% -*- erlang -*-
%%
%% Simple Erlang configuration handling library.
%%
%% Copyright 2017-2018 Jörgen Brandt
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% -------------------------------------------------------------------
%% @author Jörgen Brandt <joergen.brandt@onlinehome.de>
%% @version 0.1.4
%% @copyright 2017-2018 Jörgen Brandt
%%
%% @end
%% -------------------------------------------------------------------
-module( lib_conf ).
-export( [create_conf/5] ).
%% @doc Creates a map by combining a DefaultMap, a map extracted from a
%% ConfigFile, and a ManualMap.
%%
%% The resulting map contains only keys that were also in DefaultMap. The
%% values for any of these keys are overwritten first with the according
%% values from the ConfFile and then with the according values from the
%% ManualMap.
-spec create_conf( DefaultMap, GlobalFile, UserFile, SupplFile, FlagMap ) ->
#{ atom() => _ }
when DefaultMap :: #{ atom() => _},
GlobalFile :: undefined | string(),
UserFile :: undefined | string(),
SupplFile :: undefined | string(),
FlagMap :: #{ atom() => _ }.
create_conf( DefaultMap, GlobalFile, UserFile, SupplFile, FlagMap )
when is_map( DefaultMap ),
is_list( GlobalFile ) orelse GlobalFile =:= undefined,
is_list( UserFile ) orelse UserFile =:= undefined,
is_list( SupplFile ) orelse SupplFile =:= undefined,
is_map( FlagMap ) ->
ConfMap1 =
case GlobalFile of
undefined ->
DefaultMap;
_ ->
case file:read_file( GlobalFile ) of
% if global file does not exist use the unchanged DefaultMap
{error, enoent} ->
DefaultMap;
% report any error that is not enoent
{error, Reason1} ->
error( {Reason1, GlobalFile} );
% global file was successfully read
{ok, B1} ->
% parse the content of ConfFile to get ConfMap
GlobalMap = jsone:decode( B1, [{keys, atom}] ),
% merge default and global map giving global map precedence
merge( DefaultMap, GlobalMap )
end
end,
ConfMap2 =
case UserFile of
undefined ->
ConfMap1;
_ ->
case os:getenv( "HOME" ) of
false ->
error( {env_unset, "HOME"} );
UserDir ->
File1 = string:join( [UserDir, UserFile], "/" ),
case file:read_file( File1 ) of
% if user file does not exist use the unchanged DefaultMap
{error, enoent} ->
ConfMap1;
% report any error that is not enoent
{error, Reason2} ->
error( {Reason2, File1} );
% user file was successfully read
{ok, B2} ->
% parse the content of ConfFile to get ConfMap
UserMap = jsone:decode( B2, [{keys, atom}] ),
% merge old map and user map giving user map precedence
merge( ConfMap1, UserMap )
end
end
end,
ConfMap3 =
case SupplFile of
undefined ->
ConfMap2;
_ ->
case file:read_file( SupplFile ) of
% report any error even if it is enoent
{error, Reason3} ->
error( {Reason3, SupplFile} );
% supplement file was successfully read
{ok, B3} ->
% parse the content of supplement file to get the supplement map
SupplMap = jsone:decode( B3, [{keys, atom}] ),
% merge old map and supplement map giving supplement map precedence
merge( ConfMap2, SupplMap )
end
end,
% merge the old map and the flag map to obtain the final configuration
merge( ConfMap3, FlagMap ).
%% @doc Merges two maps.
%%
%% The returned map has all the keys from `OldMap'. Values from `NewMap'
%% supersede values from `OldMap'. Keys appearing in `NewMap' but not in
%% `OldMap' are discarded.
-spec merge( OldMap :: #{ _ => _ }, NewMap :: #{ _ => _ } ) -> #{ _ => _ }.
merge( OldMap, NewMap ) ->
F = fun( Key, OldValue ) ->
maps:get( Key, NewMap, OldValue )
end,
maps:map( F, OldMap ).