defmodule BitcoinLib.Key.HD.DerivationPath.Parser do @moduledoc """ Single purpose module that aims to simplify DerivationPath by isolating string parsing business logic """ alias BitcoinLib.Key.HD.DerivationPath alias BitcoinLib.Key.HD.DerivationPath.Parser.{Type, Purpose, CoinType, Account, Change, Index} @doc """ Single purpose function that's being called by DerivationPath.parse/1, returning a DerivationPath out of a string if the tuple starts by :ok ## Examples iex> "m/44'/0'/0'/1/0" ...> |> BitcoinLib.Key.HD.DerivationPath.Parser.parse_valid_derivation_path { :ok, %BitcoinLib.Key.HD.DerivationPath{ type: :private, purpose: :bip44, coin_type: :bitcoin, account: 0, change: :change_chain, address_index: 0 } } """ @spec parse_valid_derivation_path(binary()) :: {:ok, %DerivationPath{}} | {:error, binary()} def parse_valid_derivation_path(derivation_path) do with {:ok, derivation_path} <- validate(derivation_path), tokens <- split_path(derivation_path), {:ok, type, tokens} <- Type.extract(tokens), {:ok, purpose, tokens} <- Purpose.extract(tokens), {:ok, coin_type, tokens} <- CoinType.extract(tokens), {:ok, account, tokens} <- Account.extract(tokens), {:ok, change, tokens} <- Change.extract(tokens), {:ok, index} <- Index.extract(tokens) do { :ok, %DerivationPath{ type: type, purpose: purpose, coin_type: coin_type, account: account, change: change, address_index: index } } else {:error, message} -> {:error, message} end end defp validate(derivation_path) do trimmed_path = derivation_path |> String.replace(" ", "") case Regex.match?(~r/^(m|M)((\/(\d+\'?)*){0,5})$/, trimmed_path) do true -> {:ok, trimmed_path} false -> {:error, "Invalid derivation path"} end end defp split_path(derivation_path) do String.split(derivation_path, "/") end end