Skip to main content

lib/increase/decode.ex

defmodule Increase.Decode do
  @moduledoc """
  Shared decoding helpers used by every generated `Increase.*` struct's
  `decode/1` function. Not meant to be called directly in application
  code -- see the individual resource struct modules (e.g.
  `Increase.Accounts.Account`) instead.
  """

  @doc """
  Parses an ISO 8601 datetime string into a `DateTime`. Returns `nil`
  unchanged (for nullable fields that are absent), and falls back to the
  raw value unchanged if it doesn't parse as a valid datetime -- since
  the API is the source of truth and a parse failure here shouldn't
  crash decoding of an otherwise-valid response.
  """
  @spec datetime(String.t() | nil) :: DateTime.t() | String.t() | nil
  def datetime(nil), do: nil

  def datetime(value) when is_binary(value) do
    case DateTime.from_iso8601(value) do
      {:ok, dt, _offset} -> dt
      {:error, _reason} -> value
    end
  end

  def datetime(value), do: value

  @doc """
  Parses an ISO 8601 date string (`YYYY-MM-DD`) into a `Date`. Same
  fallback behavior as `datetime/1`.
  """
  @spec date(String.t() | nil) :: Date.t() | String.t() | nil
  def date(nil), do: nil

  def date(value) when is_binary(value) do
    case Date.from_iso8601(value) do
      {:ok, date} -> date
      {:error, _reason} -> value
    end
  end

  def date(value), do: value

  @doc """
  Decodes a single nested object with the given decoder function. Passes
  `nil` through unchanged (for nullable nested objects that are absent).
  """
  @spec struct(map() | nil, (map() -> struct())) :: struct() | nil
  def struct(nil, _decoder), do: nil
  def struct(raw, decoder) when is_map(raw), do: decoder.(raw)

  @doc """
  Decodes a list of nested objects with the given decoder function.
  Passes `nil` through unchanged (some list fields are themselves
  nullable) and decodes each element of a present list.
  """
  @spec list([map()] | nil, (map() -> struct())) :: [struct()] | nil
  def list(nil, _decoder), do: nil
  def list(raw, decoder) when is_list(raw), do: Enum.map(raw, decoder)
end