1-module(rebar3_hex_info). 2 3-export([init/1 4 ,do/1 5 ,format_error/1]). 6 7-include("rebar3_hex.hrl"). 8 9-define(PROVIDER, info). 10-define(DEPS, []). 11 12-define(ENDPOINT, "packages"). 13-define(RELEASE, "releases"). 14-define(REGISTRY_FILE, "registry"). 15 16%% =================================================================== 17%% Public API 18%% =================================================================== 19 20-spec init(rebar_state:t()) -> {ok, rebar_state:t()}. 21init(State) -> 22 Provider = providers:create([ 23 {name, ?PROVIDER}, 24 {module, ?MODULE}, 25 {namespace, hex}, 26 {bare, true}, 27 {deps, ?DEPS}, 28 {example, "rebar3 hex user <command>"}, 29 {short_desc, "Prints hex package or system information"}, 30 {desc, ""}, 31 {opts, []} 32 ]), 33 State1 = rebar_state:add_provider(State, Provider), 34 {ok, State1}. 35 36-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. 37do(State) -> 38 case rebar_state:command_args(State) of 39 [] -> 40 general(State); 41 [Package] -> 42 package(Package); 43 [Package, Version] -> 44 release(Package, Version) 45 end, 46 {ok, State}. 47 48-spec format_error(any()) -> iolist(). 49format_error(Reason) -> 50 io_lib:format("~p", [Reason]). 51 52general(State) -> 53 Deps = [ 54 case X of 55 {Name, Ver} -> {normalize(Name), Ver}; 56 Name -> {normalize(Name), get_ver(normalize(Name), State)} 57 end 58 || X <-rebar_state:get(State, deps, [])], 59 display(dedup({Deps, []}), [], State). 60 61%%prints out all deps at level 0, and 62%%For those level 0 deps which are package dependencies (Found in Hex Database), 63%%it continue printing deps at level > 0 64display([], _, _State) -> 65 ok; 66 67display([{Name, Vsn} | Deps], Listed, State) -> 68 ec_talk:say("~s ~s", [Name, Vsn]), 69 %accumulates already listed deps 70 NewListed = [{Name, Vsn} | Listed], 71 case get_deps(Name, Vsn, State) of 72 error -> 73 display(Deps, NewListed, State); 74 NewDeps -> 75 display(dedup({Deps ++ NewDeps, NewListed}), NewListed, State) 76 end. 77 78%gets deps name(s) and version(s) for a package 79get_deps(Package, Version, State) -> 80 RegistryDir = rebar_packages:registry_dir(State), 81 case ets:file2tab(filename:join(RegistryDir, ?REGISTRY_FILE)) of 82 {ok, Registry} -> 83 VerBin = ec_cnv:to_binary(Version), 84 case ets:lookup(Registry, {Package, VerBin}) of 85 [{{Package, VerBin}, [DepList, _, _]}] -> 86 [{normalize(Name), Ver} || [Name, Ver, _ , _] <- DepList]; 87 _ -> error 88 end; 89 _ -> 90 %registry table not found 91 case rebar3_hex_http:get(filename:join([?ENDPOINT, Package, "releases", Version]), []) of 92 {ok, Map} -> 93 [{normalize(X), binary_to_list(maps:get(<<"requirement">>, maps:get(X, maps:get(<<"requirements">>, Map))))} 94 || X <- maps:keys(maps:get(<<"requirements">>, Map))]; 95 {error, 404} -> error; 96 _ -> error 97 end 98 end. 99 100%gets latest release version 101%returns " " if the Package is not found in Hex 102get_ver(Package, State) -> 103 RegistryDir = rebar_packages:registry_dir(State), 104 case ets:file2tab(filename:join(RegistryDir, ?REGISTRY_FILE)) of 105 {ok, Registry} -> 106 case ets:lookup(Registry, Package) of 107 [{Package, [VerList]}] -> 108 lists:last(VerList); 109 _ -> " " 110 end; 111 _ -> 112 %registry table not found 113 case rebar3_hex_http:get(filename:join(?ENDPOINT, Package), []) of 114 {ok, Map} -> 115 Release = hd(maps:get(<<"releases">>, Map, [])), 116 binary_to_list(maps:get(<<"version">>, Release, [])); 117 {error, 404} -> 118 " "; 119 _ -> 120 " " 121 end 122 end. 123 124dedup({Deps, Listed}) -> 125 Current = Deps ++ Listed, 126 dedup(Deps, [name(Dep) || Dep <- Current]). 127 128dedup([], _) -> []; 129dedup([Dep|Deps], [Name|DepNames]) -> 130 case lists:member(Name, DepNames) of 131 true -> dedup(Deps, DepNames); 132 false -> [Dep | dedup(Deps, DepNames)] 133 end. 134 135name(T) when is_tuple(T) -> element(1, T). 136 137normalize(Name) when is_binary(Name) -> 138 Name; 139normalize(Name) when is_atom(Name) -> 140 ec_cnv:to_binary(Name). 141 142package(Package) -> 143 case rebar3_hex_http:get(filename:join(?ENDPOINT, Package), []) of 144 {ok, Map} -> 145 ec_talk:say("~s", [Package]), 146 Releases = maps:get(<<"releases">>, Map, []), 147 ec_talk:say(" Releases: ~s", [string:join([binary_to_list(maps:get(<<"version">>, X, [])) || X <- Releases], ", ")]), 148 Meta = maps:get(<<"meta">>, Map), 149 150 % Remove this when Hex no longer supports contributors 151 Contributors = maps:get(<<"contributors">>, Meta, []), 152 ec_talk:say(" Contributors: ~s", [join(Contributors)]), 153 154 Maintainers = maps:get(<<"maintainers">>, Meta, []), 155 ec_talk:say(" Maintainers: ~s", [join(Maintainers)]), 156 157 Licenses = maps:get(<<"licenses">>, Meta, []), 158 ec_talk:say(" Licenses: ~s", [join(Licenses)]), 159 160 Links = maps:to_list(maps:get(<<"links">>, Meta, [])), 161 ec_talk:say(" Links:\n ~s", [tup_list_join(Links)]), 162 163 Description = maps:get(<<"description">>, Meta, []), 164 ec_talk:say(Description, []); 165 {error, 404} -> 166 rebar_api:error("No package with name ~s", [Package]); 167 _ -> 168 rebar_api:error("Failed to retrieve package information") 169 end. 170 171 172release(Package, Version) -> 173 case rebar3_hex_http:get(filename:join([?ENDPOINT, Package, "releases", Version]), []) of 174 {ok, Map} -> 175 ec_talk:say("~s ~s", [Package, Version]), 176 ec_talk:say(" Dependencies:\n ~s", [req_join(Map)]); 177 {error, 404} -> 178 rebar_api:error("No package with name ~s", [Package]); 179 _ -> 180 rebar_api:error("Failed to retrieve package information") 181 end. 182 183join(List) -> 184 string:join([binary_to_list(X) || X <- List], ", "). 185 186tup_list_join(List) -> 187 string:join([binary_to_list(X)++": "++binary_to_list(Y) || {X, Y} <- List], "\n "). 188 189req_join(ReleaseMap) -> 190 string:join([binary_to_list(X)++": "++binary_to_list(maps:get(<<"requirement">>, maps:get(X, maps:get(<<"requirements">>, ReleaseMap)))) 191 || X <- maps:keys(maps:get(<<"requirements">>, ReleaseMap))],"\n "). 192