rebar3/src/rebar_resource_v2.erl

156 lines
5.5 KiB
Erlang

%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
-module(rebar_resource_v2).
-export([new/3,
find_resource_state/2,
format_source/1,
lock/2,
download/3,
needs_update/2,
make_vsn/3,
format_error/1]).
-export_type([resource/0,
source/0,
type/0,
location/0,
ref/0,
resource_state/0]).
-include("rebar.hrl").
-include_lib("providers/include/providers.hrl").
-type resource() :: #resource{}.
-type source() :: {type(), location(), ref()} | {type(), location(), ref(), binary()}
| {type(), location(), ref(), binary(), binary()}.
-type type() :: atom().
-type location() :: string().
-type ref() :: any().
-type resource_state() :: term().
-callback init(type(), rebar_state:t()) -> {ok, resource()}.
-callback lock(rebar_app_info:t(), resource_state()) -> source().
-callback download(file:filename_all(), rebar_app_info:t(), rebar_state:t(), resource_state()) ->
ok | {error, any()}.
-callback needs_update(rebar_app_info:t(), resource_state()) -> boolean().
-callback make_vsn(rebar_app_info:t(), resource_state()) ->
{plain, string()} | {error, string()}.
-spec new(type(), module(), term()) -> resource().
new(Type, Module, State) ->
#resource{type=Type,
module=Module,
state=State,
implementation=?MODULE}.
-spec find_resource(type(), [resource()]) -> {ok, resource()} | {error, not_found}.
find_resource(Type, Resources) ->
case ec_lists:find(fun(#resource{type=T}) -> T =:= Type end, Resources) of
error when is_atom(Type) ->
case code:which(Type) of
non_existing ->
{error, not_found};
_ ->
{ok, rebar_resource:new(Type, Type, #{})}
end;
error ->
{error, not_found};
{ok, Resource} ->
{ok, Resource}
end.
find_resource_state(Type, Resources) ->
case lists:keyfind(Type, #resource.type, Resources) of
false ->
{error, not_found};
#resource{state=State} ->
State
end.
format_source(AppInfo) ->
Name = rebar_app_info:name(AppInfo),
case rebar_app_info:source(AppInfo) of
{pkg, _Name, Vsn, _OldHash, _Hash, _} ->
io_lib:format("~ts v~s", [Name, Vsn]);
Source ->
io_lib:format("~ts (from ~p)", [Name, Source])
end.
lock(AppInfo, State) ->
resource_run(lock, rebar_app_info:source(AppInfo), [AppInfo], State).
resource_run(Function, Source, Args, State) ->
Resources = rebar_state:resources(State),
case get_resource_type(Source, Resources) of
{ok, #resource{type=_,
module=Module,
state=ResourceState,
implementation=?MODULE}} ->
erlang:apply(Module, Function, Args++[ResourceState]);
{ok, #resource{type=_,
module=Module,
state=_,
implementation=rebar_resource}} ->
erlang:apply(rebar_resource, Function, [Module | Args])
end.
download(TmpDir, AppInfo, State) ->
resource_run(download, rebar_app_info:source(AppInfo), [TmpDir, AppInfo, State], State).
needs_update(AppInfo, State) ->
resource_run(needs_update, rebar_app_info:source(AppInfo), [AppInfo], State).
%% this is a special case since it is used for project apps as well, not just deps
make_vsn(AppInfo, Vsn, State) ->
VcsType = case Vsn of {T, _} -> T; T -> T end,
Resources = rebar_state:resources(State),
case is_resource_type(VcsType, Resources) of
true ->
case find_resource(VcsType, Resources) of
{ok, #resource{type=_,
module=Module,
state=ResourceState,
implementation=?MODULE}} ->
Module:make_vsn(AppInfo, ResourceState);
{ok, #resource{type=_,
module=Module,
state=_,
implementation=rebar_resource}} ->
rebar_resource:make_vsn(Module, AppInfo)
end;
false ->
unknown
end.
format_error({no_resource, Location, Type}) ->
io_lib:format("Cannot handle dependency ~ts.~n"
" No module found for resource type ~p.", [Location, Type]);
format_error({no_resource, Source}) ->
io_lib:format("Cannot handle dependency ~ts.~n"
" No module found for unknown resource type.", [Source]).
is_resource_type(Type, Resources) ->
lists:any(fun(#resource{type=T}) -> T =:= Type end, Resources).
-spec get_resource_type(term(), [resource()]) -> {ok, resource()}.
get_resource_type({Type, Location}, Resources) ->
get_resource(Type, Location, Resources);
get_resource_type({Type, Location, _}, Resources) ->
get_resource(Type, Location, Resources);
get_resource_type({Type, _, _, Location}, Resources) ->
get_resource(Type, Location, Resources);
get_resource_type(Location={Type, _, _, _, _, _}, Resources) ->
get_resource(Type, Location, Resources);
get_resource_type(Source, _) ->
throw(?PRV_ERROR({no_resource, Source})).
-spec get_resource(type(), term(), [resource()]) -> {ok, resource()}.
get_resource(Type, Location, Resources) ->
case find_resource(Type, Resources) of
{error, not_found} ->
throw(?PRV_ERROR({no_resource, Location, Type}));
{ok, Resource} ->
{ok, Resource}
end.