defmodule Number.Phone do
@moduledoc """
Provides functions to convert numbers into formatted phone number strings.
"""
import Number.Macros, only: [is_blank: 1]
@doc """
Formats a number into a US phone number (e.g., (555) 123-9876). You can
customize the format in the options list.
## Parameters
* `number` - A float or integer to convert.
* `options` - A keyword list of options. See the documentation of all
available options below for more information.
## Options
* `:area_code` - Adds parentheses around the area code if `true`.
* `:delimiter` - Specifies the delimiter to use (defaults to “-”).
* `:extension` - Specifies an extension to add to the end of the generated number.
* `:country_code` - Sets the country code for the phone number.
Default config for these options can be specified in the `Number`
application configuration.
config :number,
phone: [
area_code: false,
delimiter: "-",
extension: nil,
country_code: nil
]
## Examples
iex> Number.Phone.number_to_phone(nil)
nil
iex> Number.Phone.number_to_phone(5551234)
"555-1234"
iex> Number.Phone.number_to_phone("5551234")
"555-1234"
iex> Number.Phone.number_to_phone(1235551234)
"123-555-1234"
iex> Number.Phone.number_to_phone(1235551234, area_code: true)
"(123) 555-1234"
iex> Number.Phone.number_to_phone(1235551234, delimiter: " ")
"123 555 1234"
iex> Number.Phone.number_to_phone(1235551234, area_code: true, extension: 555)
"(123) 555-1234 x 555"
iex> Number.Phone.number_to_phone(1235551234, area_code: true, extension: 555, country_code: 1)
"+1 (123) 555-1234 x 555"
iex> Number.Phone.number_to_phone(1235551234, country_code: 1)
"+1-123-555-1234"
iex> Number.Phone.number_to_phone("123a456")
"123a456"
iex> Number.Phone.number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
"+1.123.555.1234 x 1343"
"""
@spec number_to_phone(Number.t() | String.t(), Keyword.t()) :: String.t()
def number_to_phone(number, options \\ [])
def number_to_phone(nil, _options), do: nil
def number_to_phone(number, options) do
options = Keyword.merge(config(), options)
number
|> to_string
|> delimit_number(options[:delimiter], options[:area_code])
|> prepend_country_code(options[:country_code], options[:delimiter], options[:area_code])
|> append_extension(options[:extension])
end
defp delimit_number(number, delimiter, area_code) when area_code == false do
{:ok, leading_delimiter} = "^#{Regex.escape(delimiter)}" |> Regex.compile()
number
|> String.replace(~r/(\d{0,3})(\d{3})(\d{4})$/, "\\1#{delimiter}\\2#{delimiter}\\3")
|> String.replace(leading_delimiter, "")
end
defp delimit_number(number, delimiter, area_code) when area_code == true do
String.replace(number, ~r/(\d{1,3})(\d{3})(\d{4}$)/, "(\\1) \\2#{delimiter}\\3")
end
defp prepend_country_code(number, country_code, _, _) when is_blank(country_code), do: number
defp prepend_country_code(number, country_code, delimiter, area_code) do
if area_code,
do: "+#{country_code} #{number}",
else: "+#{country_code}#{delimiter}#{number}"
end
defp append_extension(number, extension) when is_blank(extension), do: number
defp append_extension(number, extension) do
"#{number} x #{extension}"
end
defp config do
defaults = [
area_code: false,
delimiter: "-",
extension: nil,
country_code: nil
]
Keyword.merge(defaults, Application.get_env(:number, :phone, []))
end
end