blob: 93e39f69d421483baa78b4fb261a4b524cf18e7d [file] [log] [blame]
% 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.
-module(couch_epi_data).
-include("couch_epi.hrl").
%% ------------------------------------------------------------------
%% API Function Exports
%% ------------------------------------------------------------------
-export([interval/1, data/1]).
%% ------------------------------------------------------------------
%% API Function Definitions
%% ------------------------------------------------------------------
interval(Specs) ->
extract_minimal_interval(Specs).
data(Specs) ->
Locators = locate_sources(Specs),
case lists:foldl(fun collect_data/2, {ok, [], []}, Locators) of
{ok, Hashes, Data} ->
{ok, couch_epi_util:hash(Hashes), Data};
Error ->
Error
end.
%% ------------------------------------------------------------------
%% Internal Function Definitions
%% ------------------------------------------------------------------
collect_data({App, Locator}, {ok, HashAcc, DataAcc}) ->
case definitions(Locator) of
{ok, Hash, Data} ->
{ok, [Hash | HashAcc], [{App, Data} | DataAcc]};
Error ->
Error
end;
collect_data({_App, _Locator}, Error) ->
Error.
extract_minimal_interval(Specs) ->
lists:foldl(fun minimal_interval/2, undefined, Specs).
minimal_interval({_App, #couch_epi_spec{options = Options}}, Min) ->
case lists:keyfind(interval, 1, Options) of
{interval, Interval} -> min(Interval, Min);
false -> Min
end.
locate_sources(Specs) ->
lists:map(fun({ProviderApp, #couch_epi_spec{value = Src}}) ->
{ok, Locator} = locate(ProviderApp, Src),
{ProviderApp, Locator}
end, Specs).
locate(App, {priv_file, FileName}) ->
case priv_path(App, FileName) of
{ok, FilePath} ->
ok = check_exists(FilePath),
{ok, {file, FilePath}};
Else ->
Else
end;
locate(_App, {file, FilePath}) ->
ok = check_exists(FilePath),
{ok, {file, FilePath}};
locate(_App, Locator) ->
{ok, Locator}.
priv_path(AppName, FileName) ->
case code:priv_dir(AppName) of
{error, _Error} = Error ->
Error;
Dir ->
{ok, filename:join(Dir, FileName)}
end.
check_exists(FilePath) ->
case filelib:is_regular(FilePath) of
true ->
ok;
false ->
{error, {notfound, FilePath}}
end.
definitions({file, FilePath}) ->
case file:consult(FilePath) of
{ok, Data} ->
{ok, hash_of_file(FilePath), Data};
{error, Reason} ->
{error, {FilePath, Reason}}
end;
definitions({module, Module}) when is_atom(Module) ->
definitions({module, [Module]});
definitions({module, Modules}) ->
Data = lists:append([M:data() || M <- Modules]),
Hash = couch_epi_functions_gen:hash(Modules),
{ok, Hash, Data}.
hash_of_file(FilePath) ->
{ok, Data} = file:read_file(FilePath),
crypto:hash(md5, Data).