defmodule Phone do @moduledoc ~S""" Phone is a real telephone number parser, that will help you get useful information from numbers. ## How to use Very simple to use: iex> Phone.parse("555132345678") {:ok, %{a2: "BR", a3: "BRA", country: "Brazil", international_code: "55", area_code: "51", number: "32345678", area_abbreviation: "RS", area_type: "state", area_name: "Rio Grande do Sul"}} """ import Helper.Parser @doc """ Parses a string or integer and returns a map with information about that number. ``` iex> Phone.parse("555132345678") {:ok, %{a2: "BR", a3: "BRA", country: "Brazil", international_code: "55", area_code: "51", number: "32345678", area_abbreviation: "RS", area_type: "state", area_name: "Rio Grande do Sul"}} iex> Phone.parse("+55(51)3234-5678") {:ok, %{a2: "BR", a3: "BRA", country: "Brazil", international_code: "55", area_code: "51", number: "32345678", area_abbreviation: "RS", area_type: "state", area_name: "Rio Grande do Sul"}} iex> Phone.parse("55 51 3234-5678") {:ok, %{a2: "BR", a3: "BRA", country: "Brazil", international_code: "55", area_code: "51", number: "32345678", area_abbreviation: "RS", area_type: "state", area_name: "Rio Grande do Sul"}} iex> Phone.parse(555132345678) {:ok, %{a2: "BR", a3: "BRA", country: "Brazil", international_code: "55", area_code: "51", number: "32345678", area_abbreviation: "RS", area_type: "state", area_name: "Rio Grande do Sul"}} ``` """ @spec parse(String.t() | pos_integer()) :: {:ok, map()} | {:error, String.t()} def parse(number) when is_bitstring(number) do number |> prepare_number |> Phone.Countries.build() end def parse(number) when is_integer(number) do number = Integer.to_string(number) parse(number) end def parse(_) do {:error, "Not a valid parameter, only string or integer."} end defp prepare_number(number) do number = clear(number) try do number |> String.to_integer() |> Integer.to_string() rescue _ -> "" end end defp clear(number) when is_bitstring(number) do number |> String.graphemes() |> Enum.filter(fn n -> valid_char(n) end) |> Enum.join("") end defp valid_char("+"), do: false defp valid_char("("), do: false defp valid_char(")"), do: false defp valid_char("-"), do: false defp valid_char(" "), do: false defp valid_char(_), do: true @doc """ Same as `parse/1`, except it raises on error. ``` iex> Phone.parse!("555132345678") %{a2: "BR", a3: "BRA", country: "Brazil", international_code: "55", area_code: "51", number: "32345678", area_abbreviation: "RS", area_type: "state", area_name: "Rio Grande do Sul"} iex> Phone.parse!("+55(51)3234-5678") %{a2: "BR", a3: "BRA", country: "Brazil", international_code: "55", area_code: "51", number: "32345678", area_abbreviation: "RS", area_type: "state", area_name: "Rio Grande do Sul"} iex> Phone.parse!("55 51 3234-5678") %{a2: "BR", a3: "BRA", country: "Brazil", international_code: "55", area_code: "51", number: "32345678", area_abbreviation: "RS", area_type: "state", area_name: "Rio Grande do Sul"} iex> Phone.parse!(555132345678) %{a2: "BR", a3: "BRA", country: "Brazil", international_code: "55", area_code: "51", number: "32345678", area_abbreviation: "RS", area_type: "state", area_name: "Rio Grande do Sul"} ``` """ @spec parse!(String.t() | pos_integer()) :: map() def parse!(number) when is_bitstring(number) do number = clear(number) number = try do number |> String.to_integer() |> Integer.to_string() rescue _ -> "" end Phone.Countries.build!(number) end def parse!(number) when is_integer(number) do number = Integer.to_string(number) parse!(number) end def parse!(_) do raise ArgumentError, message: "Not a valid parameter, only string or integer." end @spec parse(String.t() | pos_integer(), atom()) :: {:ok, map()} | {:error, String.t()} @spec parse!(String.t() | pos_integer(), atom()) :: map() country_parser() @doc """ Returns `true` if the number can be parsed, otherwise returns `false`. ``` iex> Phone.valid?("555132345678") true iex> Phone.valid?("+55(51)3234-5678") true iex> Phone.valid?("55 51 3234-5678") true iex> Phone.valid?(555132345678) true ``` """ @spec valid?(String.t() | pos_integer()) :: boolean() def valid?(number) when is_bitstring(number) do number |> prepare_number |> Phone.Countries.match?() end def valid?(number) when is_integer(number) do number |> to_string |> valid? end end