defmodule PostalCode do @moduledoc """ Postal code parser for the Norwegian postal code system. """ alias CSV @doc """ Accepts a postal code in string format, and returns the postal place. ## Examples iex> PostalCode.postal_code_to_place("0186") {:ok, "OSLO"} iex> PostalCode.postal_code_to_place("0638") :no_results iex> PostalCode.postal_code_to_place(0186) {:error, "Invalid type of postal code, must be string."} iex> PostalCode.postal_code_to_place("018") {:error, "Length must be 4."} """ def postal_code_to_place(postal_code) when is_binary(postal_code) and bit_size(postal_code) == 32 do case get_matching_postal_code(postal_code, :erik) do [_, place, _, _, _, _, _, _, _, _, _, _, _, _] -> {:ok, place |> String.split(" (") |> List.first()} _ -> :no_results end end def postal_code_to_place(postal_code) when is_binary(postal_code), do: {:error, "Length must be 4."} def postal_code_to_place(_postal_code), do: {:error, "Invalid type of postal code, must be string."} def postal_code_to_location(postal_code) when is_binary(postal_code) and bit_size(postal_code) == 32 do case get_matching_postal_code(postal_code, :erik) do [_, _, _, _, _, _, _, _, _, latitude, longitude, _, _, _] -> {latitude, longitude} _ -> :no_results end end def postal_code_to_location(postal_code) when is_binary(postal_code), do: {:error, "Length must be 4."} def postal_code_to_location(_postal_code), do: {:error, "Invalid type of postal code, must be string"} @doc false defp get_erik_file do Path.join(File.cwd!(), "postnummer.csv") |> File.stream!([:encoding, :utf8]) |> CSV.decode(encoding: :utf8) |> Enum.map(fn x -> case x do {:ok, list} -> Enum.at( Enum.map(list, fn y -> y |> String.replace("\r", "") |> String.split("\t") end), 0 ) end end) end defp get_matching_postal_code(postal_code, :erik) do Enum.find(get_erik_file(), fn x -> Enum.at(x, 0) == postal_code end) end end