Great suggestion Jose, here is uri.ex
with just typealiases and optional types. There are a few other modern constructs that could potentially improve even more, but I think this is aligned to what’s possible in the current scope of work.
defmodule URI do
@moduledoc """
Utilities for working with URIs.
"""
# Type aliases for better readability
typealias Scheme :: String?
typealias Host :: String?
typealias Port :: non_neg_integer?
typealias Path :: String?
typealias Query :: String?
typealias Fragment :: String?
typealias Userinfo :: String?
typealias URIString :: String
typealias EncodingType :: :www_form | :rfc3986
typealias QueryMap :: %{String => String}
typealias QueryEnum :: Enumerable.t()
typealias QueryPair :: {String, String}
typealias Predicate :: (byte -> boolean)
typealias Authority :: String?
@derive {Inspect, optional: [:authority]}
defstruct [:scheme, :authority, :userinfo, :host, :port, :path, :query, :fragment]
# Core URI functions
def default_port(scheme :: String) :: Port
def default_port(scheme :: String, port :: non_neg_integer) :: :ok
# Query encoding/decoding
def encode_query(enumerable :: QueryEnum, encoding :: EncodingType \\ :www_form) :: String
def decode_query(query :: String, map :: QueryMap \\ %{}, encoding :: EncodingType \\ :www_form) :: QueryMap
def query_decoder(query :: String, encoding :: EncodingType \\ :www_form) :: Enumerable.t()
# Character classification
def char_reserved?(character :: byte) :: boolean
def char_unreserved?(character :: byte) :: boolean
def char_unescaped?(character :: byte) :: boolean
# Encoding/decoding
def encode(string :: String, predicate :: Predicate \\ &char_unescaped?/1) :: String
def encode_www_form(string :: String) :: String
def decode(uri :: String) :: String
def decode_www_form(string :: String) :: String
# URI creation and parsing
def new(uri :: t | URIString) :: {:ok, t} | {:error, String}
def new!(uri :: t | URIString) :: t
def parse(uri :: t | String) :: t
# URI manipulation
def to_string(uri :: t) :: String
def merge(base :: t | String, rel :: t | String) :: t
def append_query(uri :: t, query :: String) :: t
def append_path(uri :: t, path :: String) :: t
# Private helper functions
defp encode_kv_pair({key, _} :: QueryPair, _encoding :: EncodingType) when is_list(key)
defp encode_kv_pair({_, value} :: QueryPair, _encoding :: EncodingType) when is_list(value)
defp encode_kv_pair({key, value} :: QueryPair, :rfc3986 :: EncodingType) :: String
defp encode_kv_pair({key, value} :: QueryPair, :www_form :: EncodingType) :: String
defp decode_query_into_map(query :: String, map :: QueryMap, encoding :: EncodingType) :: QueryMap
defp decode_query_into_dict(query :: String, dict :: any, encoding :: EncodingType) :: any
defp decode_next_query_pair("" :: String, _encoding :: EncodingType) :: nil
defp decode_next_query_pair(query :: String, encoding :: EncodingType) :: {QueryPair, String}?
defp decode_with_encoding(string :: String, :www_form :: EncodingType) :: String
defp decode_with_encoding(string :: String, :rfc3986 :: EncodingType) :: String
defp percent(char :: byte, predicate :: Predicate) :: String
defp hex(n :: byte) :: byte when n <= 9
defp hex(n :: byte) :: byte
defp unpercent(binary :: String, acc :: String, spaces :: boolean) :: String
defp hex_to_dec(n :: byte) :: byte?
defp uri_from_map(%{path: ""} = map :: map) :: t
defp uri_from_map(map :: map) :: t
defp parse_string(string :: String) :: t
defp nilify_query("?" <> query :: String) :: String
defp nilify_query(_other :: any) :: nil
defp split_authority("" :: String) :: {nil, nil, nil, nil}
defp split_authority("//" :: String) :: {Authority, nil, String, nil}
defp split_authority("//" <> authority :: String) :: {Authority, Userinfo?, Host?, Port?}
defp nilify("" :: String) :: nil
defp nilify(other :: any) :: any
defp merge_paths(nil :: nil, rel_path :: Path) :: Path
defp merge_paths(_ :: Path, "/" <> _ = rel_path :: Path) :: Path
defp merge_paths(base_path :: Path, rel_path :: Path) :: Path
defp remove_dot_segments_from_path(nil :: nil) :: nil
defp remove_dot_segments_from_path(path :: String) :: String
defp path_to_segments(path :: String) :: [String | atom]
defp remove_dot_segments(segments :: [String | atom], acc :: [String | atom]) :: [String | atom]
defp join_reversed_segments([:/] :: [atom]) :: String
defp join_reversed_segments(segments :: [String | atom]) :: String
end
defimpl String.Chars, for: URI do
def to_string(uri :: URI.t()) :: String
defp extract_authority(%{host: nil, authority: authority} :: URI.t()) :: String?
defp extract_authority(%{host: host, userinfo: userinfo, port: port} :: URI.t()) :: iodata
end