1use std::cmp::Ordering;
2use crate::{PathValue, Error};
3use std::convert::TryFrom;
4#[cfg(feature = "with-bitcoin")]
5use bitcoin::bip32::{ChildNumber};
6
7#[derive(Debug, Clone, Eq, Hash)]
10pub enum Purpose {
11 None, Pubkey, ScriptHash, Witness, Custom(u32)
16}
17
18impl PartialOrd for Purpose {
19 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
20 if self.as_value().to_raw() > other.as_value().to_raw() {
21 Some(Ordering::Greater)
22 } else if self.as_value().to_raw() == other.as_value().to_raw() {
23 Some(Ordering::Equal)
24 } else {
25 Some(Ordering::Less)
26 }
27 }
28}
29
30impl Ord for Purpose {
31 fn cmp(&self, other: &Self) -> Ordering {
32 if self.as_value().to_raw() > other.as_value().to_raw() {
33 Ordering::Greater
34 } else if self.as_value().to_raw() == other.as_value().to_raw() {
35 Ordering::Equal
36 } else {
37 Ordering::Less
38 }
39 }
40}
41
42impl PartialEq for Purpose {
43 fn eq(&self, other: &Self) -> bool {
44 self.as_value().to_raw() == other.as_value().to_raw()
45 }
46}
47
48impl Purpose {
49 pub fn as_value(&self) -> PathValue {
50 let n = match self {
51 Purpose::None => 0,
52 Purpose::Pubkey => 44,
53 Purpose::ScriptHash => 49,
54 Purpose::Witness => 84,
55 Purpose::Custom(n) => *n
56 };
57 PathValue::Hardened(n)
58 }
59}
60
61impl TryFrom<u32> for Purpose {
62 type Error = Error;
63
64 fn try_from(value: u32) -> Result<Self, Self::Error> {
65 match value {
66 44 => Ok(Purpose::Pubkey),
67 49 => Ok(Purpose::ScriptHash),
68 84 => Ok(Purpose::Witness),
69 n => if PathValue::is_ok(n) {
70 Ok(Purpose::Custom(n))
71 } else {
72 Err(Error::HighBitIsSet)
73 }
74 }
75 }
76}
77
78impl From<Purpose> for u32 {
79 fn from(value: Purpose) -> Self {
80 match value {
81 Purpose::None => 0,
82 Purpose::Pubkey => 44,
83 Purpose::ScriptHash => 49,
84 Purpose::Witness => 84,
85 Purpose::Custom(n) => n.clone()
86 }
87 }
88}
89
90impl From<&Purpose> for u32 {
91 fn from(value: &Purpose) -> Self {
92 match value {
93 Purpose::None => 0,
94 Purpose::Pubkey => 44,
95 Purpose::ScriptHash => 49,
96 Purpose::Witness => 84,
97 Purpose::Custom(n) => n.clone()
98 }
99 }
100}
101
102impl TryFrom<usize> for Purpose {
103 type Error = Error;
104
105 fn try_from(value: usize) -> Result<Self, Self::Error> {
106 Purpose::try_from(value as u32)
107 }
108}
109
110impl TryFrom<i32> for Purpose {
111 type Error = Error;
112
113 fn try_from(value: i32) -> Result<Self, Self::Error> {
114 if value < 0 {
115 return Err(Error::InvalidPurpose(0))
116 }
117 Purpose::try_from(value as u32)
118 }
119}
120
121impl TryFrom<PathValue> for Purpose {
122 type Error = Error;
123
124 fn try_from(value: PathValue) -> Result<Self, Self::Error> {
125 Purpose::try_from(value.as_number())
126 }
127}
128
129#[cfg(feature = "with-bitcoin")]
130impl From<Purpose> for ChildNumber {
131 fn from(value: Purpose) -> Self {
132 ChildNumber::from_hardened_idx(value.into()).unwrap()
133 }
134}
135
136#[cfg(feature = "with-bitcoin")]
137impl From<&Purpose> for ChildNumber {
138 fn from(value: &Purpose) -> Self {
139 ChildNumber::from_hardened_idx(value.into()).unwrap()
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use std::convert::TryFrom;
147
148 #[test]
149 pub fn create_standard_purpose() {
150 assert_eq!(Purpose::Pubkey, Purpose::try_from(44 as u32).unwrap());
151 assert_eq!(Purpose::Pubkey, Purpose::try_from(44 as usize).unwrap());
152 assert_eq!(Purpose::Pubkey, Purpose::try_from(44).unwrap());
153
154 assert_eq!(Purpose::ScriptHash, Purpose::try_from(49).unwrap());
155 assert_eq!(Purpose::Witness, Purpose::try_from(84).unwrap());
156 }
157
158 #[test]
159 pub fn create_custom_purpose() {
160 assert_eq!(Purpose::Custom(101), Purpose::try_from(101).unwrap());
161 }
162
163 #[test]
164 pub fn compare() {
165 assert!(Purpose::None < Purpose::Witness);
166 assert!(Purpose::None < Purpose::Pubkey);
167 assert!(Purpose::Pubkey < Purpose::Witness);
168 assert!(Purpose::ScriptHash < Purpose::Witness);
169 assert!(Purpose::Custom(0) < Purpose::Witness);
170 assert!(Purpose::Custom(100) > Purpose::Witness);
171 assert!(Purpose::Custom(50) > Purpose::Pubkey);
172 }
173
174 #[test]
175 pub fn order() {
176 let mut values = [
177 Purpose::Witness, Purpose::None, Purpose::Pubkey, Purpose::ScriptHash, Purpose::Pubkey,
178 Purpose::Custom(44), Purpose::Custom(84), Purpose::Custom(50), Purpose::Custom(1000)
179 ];
180 values.sort();
181 assert_eq!(
182 [
183 Purpose::None, Purpose::Pubkey, Purpose::Pubkey, Purpose::Custom(44), Purpose::ScriptHash,
184 Purpose::Custom(50), Purpose::Witness,
185 Purpose::Custom(84), Purpose::Custom(1000)
186 ],
187 values
188 )
189 }
190
191}