lib/nbp.ex

defmodule NBP do
  alias NBP.{Request,Response}

  @typedoc """
  A currency table identifier. Currency rates are split between three
  tables, A, B and C and you have to pass appropriate table identifier
  for a given currency. This is not case sensitive.
  """
  @type currency_table_t :: 'A' | 'B' | 'C' | 'a' | 'b' | 'c'

  @typedoc """
  Three-letter currency code, as in ISO 4217. This is not case sensitive.
  """
  @type currency_code_t :: charlist()

  @typedoc """
  Currency table number. Daily currency rate tables receive unique numbers once
  they are published.
  """
  @type currency_table_no_t() :: String.t()

  @doc """
  Returns the most recent exchange rate for given currency table and currency code.
  """
  @spec exchange_rate_recent(currency_table_t(), currency_code_t()) :: {:ok, NBP.Tyles.ExchangeRatesSeries.t()} | {:error, any}
  def exchange_rate_recent(currency_table, currency_code) do
    do_exchange_rate_request("/api/exchangerates/rates/#{URI.encode(to_string(currency_table))}/#{URI.encode(to_string(currency_code))}")
  end

  @doc """
  Returns last *count* of exchange rates for given currency table and currency code.
  """
  @spec exchange_rate_last(currency_table_t(), currency_code_t(), pos_integer()) :: {:ok, NBP.Tyles.ExchangeRatesSeries.t()} | {:error, any}
  def exchange_rate_last(currency_table, currency_code, count) do
    do_exchange_rate_request("/api/exchangerates/rates/#{URI.encode(to_string(currency_table))}/#{URI.encode(to_string(currency_code))}/last/#{URI.encode(to_string(count))}")
  end

  @doc """
  Returns the exchange rate for today for given currency table and currency code
  or no data if no currency rate was published for today.
  """
  @spec exchange_rate_today(currency_table_t(), currency_code_t()) :: {:ok, NBP.Tyles.ExchangeRatesSeries.t()} | {:error, any}
  def exchange_rate_today(currency_table, currency_code) do
    do_exchange_rate_request("/api/exchangerates/rates/#{URI.encode(to_string(currency_table))}/#{URI.encode(to_string(currency_code))}/today/")
  end

  @doc """
  Returns the exchange rate for given date, currency table and currency code or no data
  if no currency rates were published for given date. The oldest data available is from
  2 Jan 2022.
  """
  @spec exchange_rate_date(currency_table_t(), currency_code_t(), Date.t()) :: {:ok, NBP.Tyles.ExchangeRatesSeries.t()} | {:error, any}
  def exchange_rate_date(currency_table, currency_code, date) do
    do_exchange_rate_request("/api/exchangerates/rates/#{URI.encode(to_string(currency_table))}/#{URI.encode(to_string(currency_code))}/#{Date.to_iso8601(date)}/")
  end

  @doc """
  Returns exchange rates for date range for given currency table and currency code
  or no data if no currency rates were published for given range. The maximum range is
  93 days.
  """
  @spec exchange_rate_range(currency_table_t(), currency_code_t(), Date.t(), Date.t()) :: {:ok, NBP.Tyles.ExchangeRatesSeries.t()} | {:error, any}
  def exchange_rate_range(currency_table, currency_code, start_date, end_date) do
    do_exchange_rate_request("/api/exchangerates/rates/#{URI.encode(to_string(currency_table))}/#{URI.encode(to_string(currency_code))}/#{Date.to_iso8601(start_date)}/#{Date.to_iso8601(end_date)}/")
  end

  defp do_exchange_rate_request(path) do
    case Request.get(path) do
      {:ok, %Response{body: body}} ->
        case NBP.Parser.ExchangeRatesSeries.parse(body) do
          {:ok, result} ->
            {:ok, result}
          {:error, reason} ->
            {:error, {:parse, reason}}
        end

      {:error, reason} ->
        {:error, {:request, reason}}
    end
  end
end