defmodule BitcoinLib.Key.HD.DerivationPath.Parser.Account do
@moduledoc """
Splits the key space into independent user identities.
This is a hardened value.
See: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#account
"""
@doc """
Converts a list of path levels managed up until the account value, extracts
the account and returns the remaining levels
## Examples
iex> ["0'", "1", "2"]
...> |> BitcoinLib.Key.HD.DerivationPath.Parser.Account.extract()
{:ok, 0, ["1", "2"]}
iex> ["0", "1", "2"]
...> |> BitcoinLib.Key.HD.DerivationPath.Parser.Account.extract()
{:error, "account number must be a hardened value"}
"""
@spec extract(list()) :: {:ok, nil | integer(), list()} | {:error, binary()}
def extract([]), do: {:ok, nil, []}
def extract([account | rest]) do
with account <- String.trim(account),
{:ok, account} <- extract_hardened_value(account) do
account
|> Integer.parse()
|> convert(rest)
else
nil -> {:ok, nil}
{:error, message} -> {:error, message}
end
end
defp extract_hardened_value(""), do: {:ok, nil}
defp extract_hardened_value(hardened_value) do
{account, hardened} = String.split_at(hardened_value, -1)
case hardened do
"'" -> {:ok, account}
_ -> {:error, "account number must be a hardened value"}
end
end
defp convert({account, ""}, rest), do: {:ok, account, rest}
defp convert({_account, _remainder}, _rest), do: {:error, "account should be an integer"}
defp convert(:error, _rest), do: {:error, "account should be a valid integer"}
end