1use std::fmt;
2
3use candid::CandidType;
4use serde::{Deserialize, Serialize};
5use wasm_dbms_api::prelude::{
6 DEFAULT_ALIGNMENT, DataSize, DataType, DecodeError, Encode, MSize, MemoryError, MemoryResult,
7 PageOffset,
8};
9use wasm_dbms_macros::CustomDataType;
10
11#[derive(
16 Clone, Debug, CustomDataType, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
17)]
18#[type_tag = "principal"]
19pub struct Principal(pub candid::Principal);
20
21impl Default for Principal {
22 fn default() -> Self {
23 Self(candid::Principal::anonymous())
24 }
25}
26
27impl CandidType for Principal {
28 fn _ty() -> candid::types::Type {
29 candid::types::Type(std::rc::Rc::new(candid::types::TypeInner::Principal))
30 }
31
32 fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
33 where
34 S: candid::types::Serializer,
35 {
36 candid::Principal::idl_serialize(&self.0, serializer)
37 }
38}
39
40impl Encode for Principal {
41 const SIZE: DataSize = DataSize::Dynamic;
42
43 const ALIGNMENT: PageOffset = DEFAULT_ALIGNMENT;
44
45 fn encode(&'_ self) -> std::borrow::Cow<'_, [u8]> {
46 let principal_bytes = self.0.as_slice();
47 let mut bytes = Vec::with_capacity(1 + principal_bytes.len());
48 let len = principal_bytes.len() as u8;
49 bytes.push(len);
50 bytes.extend_from_slice(principal_bytes);
51 std::borrow::Cow::Owned(bytes)
52 }
53
54 fn decode(data: std::borrow::Cow<[u8]>) -> MemoryResult<Self>
55 where
56 Self: Sized,
57 {
58 if data.is_empty() {
59 return Err(MemoryError::DecodeError(DecodeError::TooShort));
60 }
61
62 let buf_len = data[0] as usize;
63
64 if data.len() < 1 + buf_len {
65 return Err(MemoryError::DecodeError(DecodeError::TooShort));
66 }
67
68 let principal = candid::Principal::try_from_slice(&data[1..1 + buf_len]).map_err(|e| {
69 MemoryError::DecodeError(DecodeError::IdentityDecodeError(e.to_string()))
70 })?;
71
72 Ok(Self(principal))
73 }
74
75 fn size(&self) -> MSize {
76 1 + self.0.as_slice().len() as MSize
77 }
78}
79
80impl fmt::Display for Principal {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 write!(f, "{}", self.0)
83 }
84}
85
86impl DataType for Principal {}
87
88#[cfg(test)]
89mod tests {
90
91 use wasm_dbms_api::prelude::{CustomDataType as _, Value};
92
93 use super::*;
94
95 #[test]
96 fn test_principal_encode_decode() {
97 let original = Principal(
98 candid::Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").expect("invalid principal"),
99 );
100 let encoded = original.encode();
101 let decoded = Principal::decode(encoded).unwrap();
102 assert_eq!(original, decoded);
103 }
104
105 #[test]
106 fn test_principal_encode_decode_anonymous() {
107 let original = Principal(candid::Principal::anonymous());
108 let encoded = original.encode();
109 let decoded = Principal::decode(encoded).unwrap();
110 assert_eq!(original, decoded);
111 }
112
113 #[test]
114 fn test_should_candid_encode_decode() {
115 let src = Principal(
116 candid::Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").expect("invalid principal"),
117 );
118 let buf = candid::encode_one(&src).expect("Candid encoding failed");
119 let decoded: Principal = candid::decode_one(&buf).expect("Candid decoding failed");
120 assert_eq!(src, decoded);
121 }
122
123 #[test]
124 fn test_decode_empty_data_returns_too_short() {
125 let result = Principal::decode(std::borrow::Cow::Borrowed(&[]));
126 assert!(result.is_err());
127 assert!(matches!(
128 result.unwrap_err(),
129 MemoryError::DecodeError(DecodeError::TooShort)
130 ));
131 }
132
133 #[test]
134 fn test_decode_truncated_data_returns_too_short() {
135 let data = vec![10, 0x01, 0x02];
137 let result = Principal::decode(std::borrow::Cow::Owned(data));
138 assert!(result.is_err());
139 assert!(matches!(
140 result.unwrap_err(),
141 MemoryError::DecodeError(DecodeError::TooShort)
142 ));
143 }
144
145 #[test]
146 fn test_size_returns_correct_value() {
147 let principal = Principal(
148 candid::Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").expect("invalid principal"),
149 );
150 let size = principal.size();
151 assert_eq!(size, 1 + principal.0.as_slice().len() as MSize);
153 }
154
155 #[test]
156 fn test_size_anonymous_principal() {
157 let principal = Principal(candid::Principal::anonymous());
158 let size = principal.size();
159 assert_eq!(size, 1 + principal.0.as_slice().len() as MSize);
160 }
161
162 #[test]
163 fn test_display() {
164 let inner =
165 candid::Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").expect("invalid principal");
166 let principal = Principal(inner);
167 let display = format!("{principal}");
168 assert_eq!(display, "ryjl3-tyaaa-aaaaa-aaaba-cai");
169 }
170
171 #[test]
172 fn test_default_is_anonymous() {
173 let principal = Principal::default();
174 assert_eq!(principal.0, candid::Principal::anonymous());
175 }
176
177 #[test]
178 fn test_from_principal_to_value() {
179 let principal = Principal(
180 candid::Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").expect("invalid principal"),
181 );
182 let value: Value = principal.into();
183 assert!(matches!(value, Value::Custom(_)));
184 }
185
186 #[test]
187 fn test_custom_data_type_tag() {
188 assert_eq!(Principal::TYPE_TAG, "principal");
189 }
190}