dpp/identity/identity_public_key/
purpose.rs1use crate::identity::Purpose::{
2 AUTHENTICATION, DECRYPTION, ENCRYPTION, OWNER, SYSTEM, TRANSFER, VOTING,
3};
4use anyhow::bail;
5use bincode::{Decode, DecodeUntrusted, Encode};
6#[cfg(feature = "cbor")]
7use ciborium::value::Value as CborValue;
8use serde_repr::{Deserialize_repr, Serialize_repr};
9use std::convert::TryFrom;
10
11#[repr(u8)]
12#[derive(
13 Debug,
14 PartialEq,
15 Eq,
16 Clone,
17 Copy,
18 Hash,
19 Serialize_repr,
20 Deserialize_repr,
21 Ord,
22 PartialOrd,
23 Encode,
24 Decode,
25 Default,
26 strum::EnumIter,
27 DecodeUntrusted,
28)]
29pub enum Purpose {
30 #[default]
32 AUTHENTICATION = 0,
33 ENCRYPTION = 1,
35 DECRYPTION = 2,
37 TRANSFER = 3,
40 SYSTEM = 4,
42 VOTING = 5,
44 OWNER = 6,
46}
47
48impl From<Purpose> for [u8; 1] {
49 fn from(purpose: Purpose) -> Self {
50 [purpose as u8]
51 }
52}
53
54impl From<Purpose> for &'static [u8; 1] {
55 fn from(purpose: Purpose) -> Self {
56 match purpose {
57 AUTHENTICATION => &[0],
58 ENCRYPTION => &[1],
59 DECRYPTION => &[2],
60 TRANSFER => &[3],
61 SYSTEM => &[4],
62 VOTING => &[5],
63 OWNER => &[6],
64 }
65 }
66}
67
68impl TryFrom<u8> for Purpose {
69 type Error = anyhow::Error;
70 fn try_from(value: u8) -> Result<Self, Self::Error> {
71 match value {
72 0 => Ok(AUTHENTICATION),
73 1 => Ok(ENCRYPTION),
74 2 => Ok(DECRYPTION),
75 3 => Ok(TRANSFER),
76 4 => Ok(SYSTEM),
77 5 => Ok(VOTING),
78 6 => Ok(OWNER),
79 value => bail!("unrecognized purpose: {}", value),
80 }
81 }
82}
83
84impl TryFrom<i32> for Purpose {
85 type Error = anyhow::Error;
86 fn try_from(value: i32) -> Result<Self, Self::Error> {
87 match value {
88 0 => Ok(AUTHENTICATION),
89 1 => Ok(ENCRYPTION),
90 2 => Ok(DECRYPTION),
91 3 => Ok(TRANSFER),
92 4 => Ok(SYSTEM),
93 5 => Ok(VOTING),
94 6 => Ok(OWNER),
95 value => bail!("unrecognized purpose: {}", value),
96 }
97 }
98}
99
100#[cfg(feature = "cbor")]
101impl Into<CborValue> for Purpose {
102 fn into(self) -> CborValue {
103 CborValue::from(self as u128)
104 }
105}
106impl std::fmt::Display for Purpose {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 write!(f, "{self:?}")
109 }
110}
111
112impl Purpose {
113 pub fn full_range() -> [Purpose; 6] {
115 [
116 AUTHENTICATION,
117 ENCRYPTION,
118 DECRYPTION,
119 TRANSFER,
120 VOTING,
121 OWNER,
122 ]
123 }
124 pub fn searchable_purposes() -> [Purpose; 3] {
126 [AUTHENTICATION, TRANSFER, VOTING]
127 }
128 pub fn encryption_decryption() -> [Purpose; 2] {
130 [ENCRYPTION, DECRYPTION]
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
140 fn test_purpose_try_from_u8_valid_all_variants() {
141 assert_eq!(Purpose::try_from(0u8).unwrap(), AUTHENTICATION);
142 assert_eq!(Purpose::try_from(1u8).unwrap(), ENCRYPTION);
143 assert_eq!(Purpose::try_from(2u8).unwrap(), DECRYPTION);
144 assert_eq!(Purpose::try_from(3u8).unwrap(), TRANSFER);
145 assert_eq!(Purpose::try_from(4u8).unwrap(), SYSTEM);
146 assert_eq!(Purpose::try_from(5u8).unwrap(), VOTING);
147 assert_eq!(Purpose::try_from(6u8).unwrap(), OWNER);
148 }
149
150 #[test]
152 fn test_purpose_try_from_u8_invalid() {
153 assert!(Purpose::try_from(7u8).is_err());
154 assert!(Purpose::try_from(255u8).is_err());
155 }
156
157 #[test]
159 fn test_purpose_try_from_i32_valid_all_variants() {
160 assert_eq!(Purpose::try_from(0i32).unwrap(), AUTHENTICATION);
161 assert_eq!(Purpose::try_from(1i32).unwrap(), ENCRYPTION);
162 assert_eq!(Purpose::try_from(2i32).unwrap(), DECRYPTION);
163 assert_eq!(Purpose::try_from(3i32).unwrap(), TRANSFER);
164 assert_eq!(Purpose::try_from(4i32).unwrap(), SYSTEM);
165 assert_eq!(Purpose::try_from(5i32).unwrap(), VOTING);
166 assert_eq!(Purpose::try_from(6i32).unwrap(), OWNER);
167 }
168
169 #[test]
170 fn test_purpose_try_from_i32_invalid() {
171 assert!(Purpose::try_from(-1i32).is_err());
172 assert!(Purpose::try_from(7i32).is_err());
173 assert!(Purpose::try_from(1_000_000i32).is_err());
174 }
175
176 #[test]
178 fn test_purpose_to_owned_byte_array() {
179 let arr: [u8; 1] = AUTHENTICATION.into();
180 assert_eq!(arr, [0]);
181 let arr: [u8; 1] = OWNER.into();
182 assert_eq!(arr, [6]);
183 let arr: [u8; 1] = SYSTEM.into();
184 assert_eq!(arr, [4]);
185 }
186
187 #[test]
189 fn test_purpose_to_static_byte_ref_all_variants() {
190 let r: &'static [u8; 1] = AUTHENTICATION.into();
191 assert_eq!(r, &[0u8]);
192 let r: &'static [u8; 1] = ENCRYPTION.into();
193 assert_eq!(r, &[1u8]);
194 let r: &'static [u8; 1] = DECRYPTION.into();
195 assert_eq!(r, &[2u8]);
196 let r: &'static [u8; 1] = TRANSFER.into();
197 assert_eq!(r, &[3u8]);
198 let r: &'static [u8; 1] = SYSTEM.into();
199 assert_eq!(r, &[4u8]);
200 let r: &'static [u8; 1] = VOTING.into();
201 assert_eq!(r, &[5u8]);
202 let r: &'static [u8; 1] = OWNER.into();
203 assert_eq!(r, &[6u8]);
204 }
205
206 #[test]
208 fn test_purpose_display_matches_debug_form() {
209 assert_eq!(format!("{}", AUTHENTICATION), "AUTHENTICATION");
210 assert_eq!(format!("{}", ENCRYPTION), "ENCRYPTION");
211 assert_eq!(format!("{}", DECRYPTION), "DECRYPTION");
212 assert_eq!(format!("{}", TRANSFER), "TRANSFER");
213 assert_eq!(format!("{}", SYSTEM), "SYSTEM");
214 assert_eq!(format!("{}", VOTING), "VOTING");
215 assert_eq!(format!("{}", OWNER), "OWNER");
216 }
217
218 #[test]
220 fn test_purpose_default_is_authentication() {
221 assert_eq!(Purpose::default(), AUTHENTICATION);
222 }
223
224 #[test]
226 fn test_purpose_full_range_contents() {
227 let full = Purpose::full_range();
229 assert_eq!(full.len(), 6);
230 assert!(full.contains(&AUTHENTICATION));
231 assert!(full.contains(&ENCRYPTION));
232 assert!(full.contains(&DECRYPTION));
233 assert!(full.contains(&TRANSFER));
234 assert!(full.contains(&VOTING));
235 assert!(full.contains(&OWNER));
236 assert!(!full.contains(&SYSTEM));
237 }
238
239 #[test]
240 fn test_purpose_searchable_purposes_contents() {
241 let searchable = Purpose::searchable_purposes();
242 assert_eq!(searchable.len(), 3);
243 assert_eq!(searchable, [AUTHENTICATION, TRANSFER, VOTING]);
244 }
245
246 #[test]
247 fn test_purpose_encryption_decryption_contents() {
248 let ed = Purpose::encryption_decryption();
249 assert_eq!(ed.len(), 2);
250 assert_eq!(ed, [ENCRYPTION, DECRYPTION]);
251 }
252
253 #[test]
255 fn test_purpose_round_trip_u8() {
256 for val in 0u8..=6 {
257 let p = Purpose::try_from(val).unwrap();
258 assert_eq!(p as u8, val);
259 }
260 }
261
262 #[test]
264 fn test_purpose_ordering_matches_discriminant() {
265 assert!(AUTHENTICATION < ENCRYPTION);
266 assert!(ENCRYPTION < DECRYPTION);
267 assert!(DECRYPTION < TRANSFER);
268 assert!(TRANSFER < SYSTEM);
269 assert!(SYSTEM < VOTING);
270 assert!(VOTING < OWNER);
271 }
272}