- %%% @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 when is_map(Map) -> apply_opts(Map, URIOpts);
- {error, _, _} = E -> E
- end.
- -else.
- -spec parse(URIString) -> URIMap when
- URIString :: iodata(),
- URIMap :: map() | {error, term(), 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.
|