defmodule URL.Tel do
@moduledoc """
Parses a `tel` URL
"""
import NimbleParsec
import URL.ParseHelpers.{Core, Params, Unwrap}
alias URL.ParseHelpers.Params
defstruct tel: nil, params: %{}
@type t() :: %__MODULE__{
tel: String.t(),
params: map()
}
@default_territory "US"
@doc """
Parse a URI with the `:scheme` of "tel"
## Examples
iex> tel = URI.parse "tel:+61-0407-555-987"
iex> URL.Tel.parse(tel)
{:ok, %URL.Tel{tel: "+61 407 555 987", params: %{}}}
iex> tel = URI.parse "tel:0407-555-987;phone-context=+61"
iex> URL.Tel.parse(tel)
{:ok, %URL.Tel{tel: "+61 407 555 987", params: %{"phone-context" => "+61"}}}
"""
@spec parse(URI.t()) :: {:ok, __MODULE__.t()} | {:error, {module(), binary()}}
def parse(%URI{scheme: "tel", path: path}) do
with {:ok, tel} <- unwrap(parse_tel(path)) do
tel = struct(__MODULE__, tel)
tel
|> Map.put(:tel, format(tel))
|> Params.wrap(:ok)
end
end
if Code.ensure_loaded?(ExPhoneNumber) do
defp parse_phone_number(number, territory \\ get_territory()) do
territory = if unknown_territory?(territory), do: @default_territory, else: territory
ExPhoneNumber.parse(number, to_string(territory))
end
defp unknown_territory?(territory) do
ExPhoneNumber.Metadata.get_country_code_for_region_code(to_string(territory)) == 0
end
defp format(%__MODULE__{tel: tel} = url, format \\ :international) do
phone_context = phone_context(url.params)
case parse_phone_number(phone_context <> tel) do
{:ok, tel} -> ExPhoneNumber.format(tel, format)
other -> other
end
end
defp phone_context(%{"phone-context" => phone_context}) do
with {:ok, parsed_phone_context} <- unwrap(parse_tel(phone_context)) do
Keyword.get(parsed_phone_context, :tel)
else
_ -> ""
end
end
defp phone_context(_url) do
""
end
else
defp format(%__MODULE__{tel: tel}, _format \\ :international) do
tel
end
end
@doc false
def get_territory do
cldr_territory() || gettext_territory() || @default_territory
end
if Code.ensure_loaded?(Cldr) do
defp cldr_territory do
Cldr.get_locale().territory
end
else
defp cldr_territory do
nil
end
end
if Code.ensure_loaded?(Gettext) do
defp gettext_territory do
case String.split(Gettext.get_locale(), "_") do
[_lang, territory] -> String.upcase(territory)
[_lang] -> nil
end
end
else
defp gettext_territory do
nil
end
end
defparsecp(
:parse_tel,
tel() |> concat(params())
)
end