kaspa_bip32/
derivation_path.rs1use crate::{ChildNumber, Error, Result};
4use core::{
6 fmt::{self, Display},
7 str::FromStr,
8};
9use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
10
11const PREFIX: &str = "m";
13
14#[derive(Clone, Debug, Default, Eq, PartialEq)]
16pub struct DerivationPath {
17 path: Vec<ChildNumber>,
18}
19
20impl<'de> Deserialize<'de> for DerivationPath {
21 fn deserialize<D>(deserializer: D) -> std::result::Result<DerivationPath, D::Error>
22 where
23 D: Deserializer<'de>,
24 {
25 struct DerivationPathVisitor;
26 impl<'de> de::Visitor<'de> for DerivationPathVisitor {
27 type Value = DerivationPath;
28 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
29 formatter.write_str("a string containing list of permissions separated by a '+'")
30 }
31
32 fn visit_str<E>(self, value: &str) -> std::result::Result<Self::Value, E>
33 where
34 E: de::Error,
35 {
36 DerivationPath::from_str(value).map_err(|err| de::Error::custom(err.to_string()))
37 }
38 fn visit_borrowed_str<E>(self, v: &'de str) -> std::result::Result<Self::Value, E>
39 where
40 E: de::Error,
41 {
42 DerivationPath::from_str(v).map_err(|err| de::Error::custom(err.to_string()))
43 }
44 }
45
46 deserializer.deserialize_str(DerivationPathVisitor)
47 }
48}
49
50impl Serialize for DerivationPath {
51 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
52 where
53 S: Serializer,
54 {
55 serializer.serialize_str(&self.to_string())
56 }
57}
58
59impl DerivationPath {
60 pub fn iter(&self) -> impl Iterator<Item = ChildNumber> + '_ {
62 self.path.iter().cloned()
63 }
64
65 pub fn is_empty(&self) -> bool {
67 self.path.is_empty()
68 }
69
70 pub fn len(&self) -> usize {
72 self.path.len()
73 }
74
75 pub fn parent(&self) -> Option<Self> {
79 self.path.len().checked_sub(1).map(|n| {
80 let mut parent = self.clone();
81 parent.path.truncate(n);
82 parent
83 })
84 }
85
86 pub fn push(&mut self, child_number: ChildNumber) {
88 self.path.push(child_number)
89 }
90}
91
92impl AsRef<[ChildNumber]> for DerivationPath {
93 fn as_ref(&self) -> &[ChildNumber] {
94 &self.path
95 }
96}
97
98impl Display for DerivationPath {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.write_str(PREFIX)?;
101
102 for child_number in self.iter() {
103 write!(f, "/{}", child_number)?;
104 }
105
106 Ok(())
107 }
108}
109
110impl Extend<ChildNumber> for DerivationPath {
111 fn extend<T>(&mut self, iter: T)
112 where
113 T: IntoIterator<Item = ChildNumber>,
114 {
115 self.path.extend(iter);
116 }
117}
118
119impl FromStr for DerivationPath {
120 type Err = Error;
121
122 fn from_str(path: &str) -> Result<DerivationPath> {
123 let mut path = path.split('/');
124
125 if path.next() != Some(PREFIX) {
126 return Err(Error::String(format!("Derivation don't start with `{PREFIX}/`")));
127 }
128
129 Ok(DerivationPath { path: path.map(str::parse).collect::<Result<_>>()? })
130 }
131}
132
133impl IntoIterator for DerivationPath {
134 type Item = ChildNumber;
135 type IntoIter = std::vec::IntoIter<ChildNumber>;
136
137 fn into_iter(self) -> std::vec::IntoIter<ChildNumber> {
138 self.path.into_iter()
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::DerivationPath;
145 #[test]
150 fn round_trip() {
151 let path_m = "m";
152 assert_eq!(path_m.parse::<DerivationPath>().unwrap().to_string(), path_m);
153
154 let path_m_0 = "m/0";
155 assert_eq!(path_m_0.parse::<DerivationPath>().unwrap().to_string(), path_m_0);
156
157 let path_m_0_2147483647h = "m/0/2147483647'";
158 assert_eq!(path_m_0_2147483647h.parse::<DerivationPath>().unwrap().to_string(), path_m_0_2147483647h);
159
160 let path_m_0_2147483647h_1 = "m/0/2147483647'/1";
161 assert_eq!(path_m_0_2147483647h_1.parse::<DerivationPath>().unwrap().to_string(), path_m_0_2147483647h_1);
162
163 let path_m_0_2147483647h_1_2147483646h = "m/0/2147483647'/1/2147483646'";
164 assert_eq!(
165 path_m_0_2147483647h_1_2147483646h.parse::<DerivationPath>().unwrap().to_string(),
166 path_m_0_2147483647h_1_2147483646h
167 );
168
169 let path_m_0_2147483647h_1_2147483646h_2 = "m/0/2147483647'/1/2147483646'/2";
170 assert_eq!(
171 path_m_0_2147483647h_1_2147483646h_2.parse::<DerivationPath>().unwrap().to_string(),
172 path_m_0_2147483647h_1_2147483646h_2
173 );
174 }
175
176 #[test]
177 fn parent() {
178 let path_m_0_2147483647h = "m/0/2147483647'".parse::<DerivationPath>().unwrap();
179 let path_m_0 = path_m_0_2147483647h.parent().unwrap();
180 assert_eq!("m/0", path_m_0.to_string());
181
182 let path_m = path_m_0.parent().unwrap();
183 assert_eq!("m", path_m.to_string());
184 assert_eq!(path_m.parent(), None);
185 }
186}