miden_protocol/account/
access.rs1use alloc::fmt;
2use core::cmp::Ordering;
3
4use crate::Felt;
5use crate::errors::RoleSymbolError;
6use crate::utils::ShortCapitalString;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct RoleSymbol(ShortCapitalString);
22
23impl RoleSymbol {
24 pub const ALPHABET: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_";
26
27 pub const MIN_ENCODED_VALUE: u64 = 1;
31
32 pub const MAX_ENCODED_VALUE: u64 = 4052555153018976252;
36
37 pub fn new_unchecked(role_symbol: &str) -> Self {
45 Self::new(role_symbol).expect("invalid role symbol")
46 }
47
48 pub fn new(role_symbol: &str) -> Result<Self, RoleSymbolError> {
55 ShortCapitalString::from_ascii_uppercase_and_underscore(role_symbol)
56 .map(Self)
57 .map_err(Into::into)
58 }
59
60 pub fn as_element(&self) -> Felt {
62 self.0.as_element(Self::ALPHABET).expect("RoleSymbol alphabet is always valid")
63 }
64}
65
66impl Ord for RoleSymbol {
67 fn cmp(&self, other: &Self) -> Ordering {
68 self.as_element().as_canonical_u64().cmp(&other.as_element().as_canonical_u64())
69 }
70}
71
72impl PartialOrd for RoleSymbol {
73 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
74 Some(self.cmp(other))
75 }
76}
77
78impl fmt::Display for RoleSymbol {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 self.0.fmt(f)
81 }
82}
83
84impl From<RoleSymbol> for Felt {
85 fn from(role_symbol: RoleSymbol) -> Self {
86 role_symbol.as_element()
87 }
88}
89
90impl From<&RoleSymbol> for Felt {
91 fn from(role_symbol: &RoleSymbol) -> Self {
92 role_symbol.as_element()
93 }
94}
95
96impl TryFrom<&str> for RoleSymbol {
97 type Error = RoleSymbolError;
98
99 fn try_from(role_symbol: &str) -> Result<Self, Self::Error> {
100 Self::new(role_symbol)
101 }
102}
103
104impl TryFrom<Felt> for RoleSymbol {
105 type Error = RoleSymbolError;
106
107 fn try_from(felt: Felt) -> Result<Self, Self::Error> {
109 ShortCapitalString::try_from_encoded_felt(
110 felt,
111 Self::ALPHABET,
112 Self::MIN_ENCODED_VALUE,
113 Self::MAX_ENCODED_VALUE,
114 )
115 .map(Self)
116 .map_err(Into::into)
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use alloc::string::ToString;
123
124 use assert_matches::assert_matches;
125
126 use super::{Felt, RoleSymbol};
127 use crate::errors::RoleSymbolError;
128
129 #[test]
130 fn test_role_symbol_roundtrip_and_validation() {
131 let role_symbols = ["MINTER", "BURNER", "MINTER_ADMIN", "A", "A_B_C"];
132 for role_symbol in role_symbols {
133 let encoded: Felt = RoleSymbol::new(role_symbol).unwrap().into();
134 let decoded = RoleSymbol::try_from(encoded).unwrap();
135 assert_eq!(decoded.to_string(), role_symbol);
136 }
137
138 assert_matches!(RoleSymbol::new("").unwrap_err(), RoleSymbolError::InvalidLength(0));
139 assert_matches!(
140 RoleSymbol::new("ABCDEFGHIJKLM").unwrap_err(),
141 RoleSymbolError::InvalidLength(13)
142 );
143 assert_matches!(
144 RoleSymbol::new("MINTER-ADMIN").unwrap_err(),
145 RoleSymbolError::InvalidCharacter
146 );
147 assert_matches!(RoleSymbol::new("mINTER").unwrap_err(), RoleSymbolError::InvalidCharacter);
148 }
149
150 #[test]
151 fn test_role_symbol_ordering_matches_encoded_felt() {
152 let ab = RoleSymbol::new("AB").unwrap();
153 let b = RoleSymbol::new("B").unwrap();
154
155 assert!(ab.as_element().as_canonical_u64() > b.as_element().as_canonical_u64());
158 assert!(ab > b);
159 assert!(b < ab);
160 }
161}