calimero_primitives/
alias.rs1#[cfg(test)]
2#[path = "tests/alias.rs"]
3mod tests;
4
5use core::cmp::Ordering as CmpOrdering;
6use core::hash::{Hash, Hasher};
7use core::marker::PhantomData;
8use core::str::{from_utf8, from_utf8_unchecked, FromStr};
9use std::fmt;
10
11use serde::{de, ser, Deserialize, Serialize};
12use thiserror::Error;
13
14use crate::application::ApplicationId;
15use crate::context::ContextId;
16use crate::identity::PublicKey;
17
18const MAX_ALIAS_LEN: usize = 50;
19
20const _: () = {
22 assert!(
25 !(MAX_ALIAS_LEN > u8::MAX as usize),
26 "MAX_ALIAS_LEN must be a value that fits in 8 bits."
27 );
28};
29
30pub trait ScopedAlias {
31 type Scope;
32}
33
34impl ScopedAlias for ContextId {
35 type Scope = ();
36}
37
38impl ScopedAlias for PublicKey {
39 type Scope = ContextId;
40}
41
42impl ScopedAlias for ApplicationId {
43 type Scope = ();
44}
45
46pub struct Alias<T> {
47 str: [u8; MAX_ALIAS_LEN],
48 len: u8,
49 _pd: PhantomData<T>,
50}
51
52impl<T> Copy for Alias<T> {}
53impl<T> Clone for Alias<T> {
54 fn clone(&self) -> Self {
55 *self
56 }
57}
58
59#[derive(Copy, Clone, Debug, Error)]
60#[error("invalid alias: {}")]
61pub enum InvalidAlias {
62 #[error("exceeds maximum length of {} characters", MAX_ALIAS_LEN)]
63 TooLong,
64}
65
66impl<T> Alias<T> {
67 #[must_use]
73 pub fn new(s: &str) -> Self {
74 s.parse().expect("alias too long")
75 }
76
77 pub fn try_from_str(s: &str) -> Result<Self, InvalidAlias> {
79 s.parse()
80 }
81
82 #[must_use]
83 pub fn as_str(&self) -> &str {
84 let bytes = &self.str[..self.len as usize];
85 unsafe { from_utf8_unchecked(bytes) }
86 }
87}
88
89impl<T> AsRef<str> for Alias<T> {
90 fn as_ref(&self) -> &str {
91 self.as_str()
92 }
93}
94
95impl<T> FromStr for Alias<T> {
96 type Err = InvalidAlias;
97
98 fn from_str(s: &str) -> Result<Self, Self::Err> {
99 if s.len() > MAX_ALIAS_LEN {
100 return Err(InvalidAlias::TooLong);
101 }
102
103 let mut str = [0; MAX_ALIAS_LEN];
104 str[..s.len()].copy_from_slice(s.as_bytes());
105
106 let len_u8 = u8::try_from(s.len()).map_err(|_| InvalidAlias::TooLong)?;
109
110 Ok(Self {
111 str,
112 len: len_u8,
113 _pd: PhantomData,
114 })
115 }
116}
117
118impl<T> fmt::Display for Alias<T> {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 f.pad(self.as_str())
121 }
122}
123
124impl<T> fmt::Debug for Alias<T> {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 f.debug_tuple("Alias").field(&self.as_str()).finish()
127 }
128}
129
130impl<T> Eq for Alias<T> {}
131
132impl<T> PartialEq for Alias<T> {
133 fn eq(&self, other: &Self) -> bool {
134 self.str == other.str
135 }
136}
137
138impl<T> Ord for Alias<T> {
139 fn cmp(&self, other: &Self) -> CmpOrdering {
140 self.str.cmp(&other.str)
141 }
142}
143
144impl<T> PartialOrd for Alias<T> {
145 fn partial_cmp(&self, other: &Self) -> Option<CmpOrdering> {
146 Some(self.cmp(other))
147 }
148}
149
150impl<T> Serialize for Alias<T> {
151 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
152 where
153 S: ser::Serializer,
154 {
155 serializer.serialize_str(self.as_str())
156 }
157}
158
159impl<'de, T> Deserialize<'de> for Alias<T> {
160 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
161 where
162 D: de::Deserializer<'de>,
163 {
164 struct AliasVisitor<T>(PhantomData<T>);
165
166 impl<T> de::Visitor<'_> for AliasVisitor<T> {
167 type Value = Alias<T>;
168
169 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 write!(f, "an alias of at most {MAX_ALIAS_LEN} characters")
171 }
172
173 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
174 where
175 E: de::Error,
176 {
177 Alias::from_str(v).map_err(de::Error::custom)
178 }
179
180 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
181 where
182 E: de::Error,
183 {
184 let Ok(s) = from_utf8(v) else {
185 return Err(de::Error::invalid_value(de::Unexpected::Bytes(v), &self));
186 };
187
188 Alias::from_str(s).map_err(de::Error::custom)
189 }
190 }
191
192 deserializer.deserialize_str(AliasVisitor(PhantomData))
193 }
194}
195
196impl<T> Hash for Alias<T> {
197 fn hash<H: Hasher>(&self, state: &mut H) {
198 self.as_str().hash(state);
199 }
200}