lib/key/hd/derivation_path/parser/index.ex

defmodule BitcoinLib.Key.HD.DerivationPath.Parser.Index do
  @moduledoc """
  Addresses are numbered from index 0 in sequentially increasing manner. This number is used as child index in BIP32 derivation.

  Public derivation is used at this level.

  See: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#index
  """

  @doc """
  Converts a list of path levels managed up until the index value, extracts
  the index

  ## Examples
      iex> ["2"]
      ...> |> BitcoinLib.Key.HD.DerivationPath.Parser.Index.extract()
      {:ok, 2}
  """
  @spec extract(list()) :: {:ok, nil} | {:ok, integer()} | {:error, binary()}
  def extract([]), do: {:ok, nil}

  def extract([index]) do
    with {:ok, index} <- validate_non_hardened(index),
         {integer_index, ""} <- Integer.parse(index) do
      {:ok, integer_index}
    else
      {:error, message} -> {:error, message}
      :error -> {:error, "index should be a valid integer"}
    end
  end

  defp validate_non_hardened(index) do
    case String.ends_with?(index, "'") do
      true -> {:error, "account number must NOT be a hardened value"}
      false -> {:ok, index}
    end
  end
end