use crate::index::DerivationIndex; use serde::{Deserialize, Serialize}; use thiserror::Error; /// Errors associated with creating a [`DerivationPath`]. #[derive(Error, Debug)] pub enum Error { /// A [`DerivationIndex`] was not able to be created. #[error("Unable to create index: {0}")] UnableToCreateIndex(#[from] super::index::Error), /// The path could not be parsed due to a bad prefix. Paths must be in the format: /// /// m [/ index [']]+ /// /// The prefix for the path must be `m`, and all indices must be integers between 0 and /// 2^31. #[error("Unable to parse path due to bad path prefix")] UnknownPathPrefix, } type Result = std::result::Result; const PREFIX: &str = "m"; /// A fully qualified path to derive a key. #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct DerivationPath { pub(crate) path: Vec, } impl DerivationPath { /// Returns an iterator over the [`DerivationPath`]. pub fn iter(&self) -> impl Iterator { self.path.iter() } pub fn len(&self) -> usize { self.path.len() } pub fn is_empty(&self) -> bool { self.path.is_empty() } } impl std::str::FromStr for DerivationPath { type Err = Error; fn from_str(s: &str) -> Result { let mut iter = s.split('/'); if iter.next() != Some(PREFIX) { return Err(Error::UnknownPathPrefix); } Ok(Self { path: iter .map(DerivationIndex::from_str) .map(|maybe_err| maybe_err.map_err(From::from)) .collect::>()?, }) } } impl std::fmt::Display for DerivationPath { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{PREFIX}")?; for index in self.iter() { write!(f, "/{index}")?; } Ok(()) } } #[cfg(test)] mod tests { use super::*; use std::str::FromStr; #[test] #[should_panic] fn requires_master_path() { DerivationPath::from_str("1234/5678'").unwrap(); } #[test] fn equivalency() -> Result<()> { let paths = ["m/1234'/5678", "m/44'/0'/0'", "m"]; for path in paths { assert_eq!(&DerivationPath::from_str(path)?.to_string(), path); } Ok(()) } }