|
|
@ -0,0 +1,130 @@ |
|
|
|
%%% @doc multi-OTP version compatibility shim for working with URIs |
|
|
|
-module(rebar_uri). |
|
|
|
|
|
|
|
-export([ |
|
|
|
parse/1, parse/2, scheme_defaults/0, |
|
|
|
append_path/2 |
|
|
|
]). |
|
|
|
|
|
|
|
-ifdef(OTP_RELEASE). |
|
|
|
-spec parse(URIString) -> URIMap when |
|
|
|
URIString :: uri_string:uri_string(), |
|
|
|
URIMap :: uri_string:uri_map() | uri_string:error(). |
|
|
|
|
|
|
|
parse(URIString) -> |
|
|
|
parse(URIString, []). |
|
|
|
|
|
|
|
parse(URIString, URIOpts) -> |
|
|
|
case uri_string:parse(URIString) of |
|
|
|
#{path := ""} = Map -> apply_opts(Map#{path => "/"}, URIOpts); |
|
|
|
Map -> apply_opts(Map, URIOpts) |
|
|
|
end. |
|
|
|
-else. |
|
|
|
-spec parse(URIString) -> URIMap when |
|
|
|
URIString :: iodata(), |
|
|
|
URIMap :: map() | {error, atom(), term()}. |
|
|
|
|
|
|
|
parse(URIString) -> |
|
|
|
parse(URIString, []). |
|
|
|
|
|
|
|
parse(URIString, URIOpts) -> |
|
|
|
case http_uri:parse(URIString, URIOpts) of |
|
|
|
{error, Reason} -> |
|
|
|
%% no additional parser/term info available to us, |
|
|
|
%% e.g. see what uri_string returns in |
|
|
|
%% uri_string:parse(<<"h$ttp:::://////lolz">>). |
|
|
|
{error, Reason, ""}; |
|
|
|
{ok, {Scheme, UserInfo, Host, Port, Path, Query}} -> |
|
|
|
#{ |
|
|
|
scheme => rebar_utils:to_list(Scheme), |
|
|
|
host => Host, |
|
|
|
port => Port, |
|
|
|
path => Path, |
|
|
|
%% http_uri:parse/1 includes the leading question mark |
|
|
|
%% in query string but uri_string:parse/1 leaves it out. |
|
|
|
%% string:slice/2 isn't available in OTP <= 19. |
|
|
|
query => case Query of |
|
|
|
[] -> ""; |
|
|
|
_ -> string:substr(Query, 2) |
|
|
|
end, |
|
|
|
userinfo => UserInfo |
|
|
|
} |
|
|
|
end. |
|
|
|
-endif. |
|
|
|
|
|
|
|
%% OTP 21+ |
|
|
|
-ifdef(OTP_RELEASE). |
|
|
|
append_path(Url, ExtraPath) -> |
|
|
|
case parse(Url) of |
|
|
|
#{path := Path} = Map -> |
|
|
|
FullPath = join(Path, ExtraPath), |
|
|
|
{ok, uri_string:recompose(maps:update(path, FullPath, Map))}; |
|
|
|
_ -> |
|
|
|
error |
|
|
|
end. |
|
|
|
-else. |
|
|
|
append_path(Url, ExtraPath) -> |
|
|
|
case parse(Url) of |
|
|
|
#{scheme := Scheme, userinfo := UserInfo, host := Host, |
|
|
|
port := Port, path := Path, query := Query} -> |
|
|
|
ListScheme = rebar_utils:to_list(Scheme), |
|
|
|
PrefixedQuery = case Query of |
|
|
|
[] -> []; |
|
|
|
Other -> lists:append(["?", Other]) |
|
|
|
end, |
|
|
|
NormPath = case Path of |
|
|
|
"" -> "/"; |
|
|
|
_ -> Path |
|
|
|
end, |
|
|
|
{ok, maybe_port( |
|
|
|
Url, lists:append([ListScheme, "://", UserInfo, Host]), |
|
|
|
[$: | rebar_utils:to_list(Port)], |
|
|
|
lists:append([join(NormPath, ExtraPath), PrefixedQuery]) |
|
|
|
)}; |
|
|
|
_ -> |
|
|
|
error |
|
|
|
end. |
|
|
|
-endif. |
|
|
|
|
|
|
|
%% OTP 21+ |
|
|
|
-ifdef(OTP_RELEASE). |
|
|
|
scheme_defaults() -> |
|
|
|
%% no scheme defaults here; just custom ones |
|
|
|
[]. |
|
|
|
-else. |
|
|
|
scheme_defaults() -> |
|
|
|
http_uri:scheme_defaults(). |
|
|
|
-endif. |
|
|
|
|
|
|
|
join(URI, "") -> URI; |
|
|
|
join(URI, "/") -> URI; |
|
|
|
join("/", [$/|_] = Path) -> Path; |
|
|
|
join("/", Path) -> [$/ | Path]; |
|
|
|
join("", [$/|_] = Path) -> Path; |
|
|
|
join("", Path) -> [$/ | Path]; |
|
|
|
join([H|T], Path) -> [H | join(T, Path)]. |
|
|
|
|
|
|
|
|
|
|
|
-ifdef(OTP_RELEASE). |
|
|
|
apply_opts(Map = #{port := _}, _) -> |
|
|
|
Map; |
|
|
|
apply_opts(Map = #{scheme := Scheme}, URIOpts) -> |
|
|
|
SchemeDefaults = proplists:get_value(scheme_defaults, URIOpts, []), |
|
|
|
%% Here is the funky bit: don't add the port number if it's in a default |
|
|
|
%% to maintain proper default behaviour. |
|
|
|
try lists:keyfind(list_to_existing_atom(Scheme), 1, SchemeDefaults) of |
|
|
|
{_, Port} -> |
|
|
|
Map#{port => Port}; |
|
|
|
false -> |
|
|
|
Map |
|
|
|
catch |
|
|
|
error:badarg -> % not an existing atom, not in the list |
|
|
|
Map |
|
|
|
end. |
|
|
|
-else. |
|
|
|
maybe_port(Url, Host, Port, PathQ) -> |
|
|
|
case lists:prefix(Host ++ Port, Url) of |
|
|
|
true -> Host ++ Port ++ PathQ; % port was explicit |
|
|
|
false -> Host ++ PathQ % port was implicit |
|
|
|
end. |
|
|
|
-endif. |