1pub mod crock_ford {
2
3 use bytes::BytesMut;
4 use lazy_static::lazy_static;
5 use num_bigint::{BigUint, ToBigUint};
6 use ring::rand::{SecureRandom, SystemRandom};
7
8 const BYTE_SIZE: usize = 15;
9 const CROCKFORD_CHECKSUM_CHARS: &str = "0123456789ABCDEFGHJKMNPQRSTVWXYZ*~$=U";
10 const CROCKFORD_MODULO_PRIME: usize = 37;
12
13 fn rng() -> &'static dyn SecureRandom {
14 use std::ops::Deref;
15
16 lazy_static! {
17 static ref RANDOM: SystemRandom = SystemRandom::new();
18 }
19
20 RANDOM.deref()
21 }
22
23 #[derive(Debug)]
24 struct Bytes(BytesMut);
25
26 impl Bytes {
27 pub fn to_slice(&self) -> &[u8] {
28 &self.0[..]
29 }
30
31 pub fn to_int(&self) -> BigUint {
32 BigUint::from_bytes_be(&self.0[..])
33 }
34
35 pub fn to_vec(&self) -> Vec<u8> {
36 self.0.to_vec()
37 }
38
39 pub fn derive_crockford_checksum(&self) -> BigUint {
40 self.to_int() % ToBigUint::to_biguint(&CROCKFORD_MODULO_PRIME).unwrap()
41 }
42
43 pub fn new(size: usize) -> Result<Self, String> {
44 let mut bytes = vec![0; size];
45 rng().fill(&mut bytes).map_err(|e| e.to_string())?;
46 Ok(Self(BytesMut::from_iter(bytes.iter())))
47 }
48 }
49
50 impl TryFrom<BigUint> for Bytes {
51 type Error = &'static str;
52 fn try_from(value: BigUint) -> Result<Self, Self::Error> {
53 let bytes = value.to_bytes_be();
54 Bytes::try_from(bytes)
55 }
56 }
57
58 impl TryFrom<Vec<u8>> for Bytes {
59 type Error = &'static str;
60 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
61 let bytes = BytesMut::try_from(&value[..])
62 .map_err(|_| "unable to convert value to mutable byte")?;
63 Ok(Self(bytes))
64 }
65 }
66
67 #[derive(Debug)]
68 pub struct Uuid {
69 bytes: Bytes,
70 checksum: BigUint,
71 }
72
73 impl Uuid {
74 pub fn new() -> Self {
75 let bytes = Bytes::new(BYTE_SIZE).expect("failed to generate random bytes");
76 let checksum = bytes.derive_crockford_checksum();
77 Self { bytes, checksum }
78 }
79
80 pub fn value(&self) -> String {
81 base32::encode(base32::Alphabet::Crockford, &self.bytes.to_slice())
82 }
83
84 fn get_checksum_char(checksum: &BigUint) -> char {
85 let checksum: i8 = checksum.try_into().unwrap();
86 CROCKFORD_CHECKSUM_CHARS
87 .chars()
88 .nth(checksum.abs() as usize)
89 .unwrap()
90 }
91
92 fn value_with_checksum(&self) -> String {
93 format!(
94 "{}{}",
95 self.value(),
96 Uuid::get_checksum_char(&self.checksum)
97 )
98 }
99
100 fn len() -> usize {
101 (BYTE_SIZE * 8 / 5) + 1
103 }
104
105 fn from_str(value: &str) -> Result<Self, &'static str> {
106 if value.len() != Uuid::len() {
107 return Err("invalid string length");
108 }
109
110 let value = value.to_ascii_uppercase();
111
112 let id = &value[..=(Uuid::len() - 2)];
113 let bytes = match base32::decode(base32::Alphabet::Crockford, id) {
114 None => return Err("invalid uuid str"),
115 Some(d) => Bytes::try_from(d)?,
116 };
117
118 let checksum = bytes.derive_crockford_checksum();
119 if Uuid::get_checksum_char(&checksum)
120 == value[(Uuid::len() - 1)..].chars().nth(0).unwrap()
121 {
122 Ok(Self { bytes, checksum })
123 } else {
124 Err("invalid uuid str")
125 }
126 }
127 }
128
129 impl std::fmt::Display for Uuid {
130 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
131 write!(f, "{}", self.value_with_checksum())
132 }
133 }
134
135 impl TryFrom<&str> for Uuid {
136 type Error = &'static str;
137 fn try_from(value: &str) -> Result<Self, Self::Error> {
138 Uuid::from_str(value)
139 }
140 }
141
142 impl TryFrom<String> for Uuid {
143 type Error = &'static str;
144 fn try_from(value: String) -> Result<Self, Self::Error> {
145 Uuid::from_str(value.as_str())
146 }
147 }
148
149 impl TryFrom<BigUint> for Uuid {
150 type Error = &'static str;
151 fn try_from(value: BigUint) -> Result<Self, Self::Error> {
152 let bytes: Bytes = value
153 .try_into()
154 .map_err(|_| "unable to convert bigint to uuid")?;
155 let checksum = bytes.derive_crockford_checksum();
156 Ok(Self { bytes, checksum })
157 }
158 }
159
160 impl Into<BigUint> for Uuid {
161 fn into(self) -> BigUint {
162 self.bytes.to_int()
163 }
164 }
165
166 impl Into<Vec<u8>> for Uuid {
167 fn into(self) -> Vec<u8> {
168 self.bytes.to_vec()
169 }
170 }
171
172 impl Into<Bytes> for Uuid {
173 fn into(self) -> Bytes {
174 self.bytes
175 }
176 }
177
178 impl PartialEq<Uuid> for Uuid {
179 fn eq(&self, other: &Uuid) -> bool {
180 self.value_with_checksum() == other.value_with_checksum()
181 }
182 }
183
184 impl PartialEq<String> for Uuid {
185 fn eq(&self, other: &String) -> bool {
186 match Uuid::from_str(other) {
187 Ok(uuid) => *self == uuid,
188 Err(_) => false,
189 }
190 }
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use crate::crock_ford::Uuid;
197 use num_bigint::BigUint;
198
199 fn str_uuid() -> &'static str {
200 "4s0y2vz7sf4vghnznytz9gvq6"
201 }
202
203 #[test]
204 fn generate() {
205 let uuid = Uuid::new();
206 println!("uuid={}", uuid);
207 assert_eq!(uuid.to_string().len(), 25); }
209
210 #[test]
211 fn generate_from_string() {
212 let result: Uuid = str_uuid().try_into().unwrap();
213 assert_eq!(result.to_string().to_lowercase(), str_uuid());
214 }
215
216 #[test]
217 fn compare_two_uuid_of_same_value() {
218 let first: Uuid = str_uuid().try_into().unwrap();
219 let second: Uuid = str_uuid().try_into().unwrap();
220 assert_eq!(first, second);
221 }
222
223 #[test]
224 fn compare_two_uuid_of_diff_value() {
225 let first: Uuid = str_uuid().try_into().unwrap();
226 let second = Uuid::new();
227 assert_ne!(first, second);
228 }
229
230 #[test]
231 fn compare_uuid_with_string() {
232 let uuid: Uuid = str_uuid().try_into().unwrap();
233 assert_eq!(uuid, str_uuid().to_string());
234 }
235
236 #[test]
237 fn get_uuid_as_integer_value() {
238 let uuid: Uuid = str_uuid().try_into().unwrap();
239 let int_value: BigUint = uuid.into();
240 println!("{}", int_value);
241 }
242
243 #[test]
245 fn get_uuid_as_byte_value() {
246 let uuid: Vec<u8> = Uuid::new().into();
247 println!("{:?}", uuid);
248 }
249
250 #[test]
252 fn convert_integer_to_uuid() {
253 let int_value: BigUint = Uuid::try_from(str_uuid()).unwrap().try_into().unwrap();
254 let uuid: Uuid = int_value.try_into().unwrap();
255 assert_eq!(uuid, str_uuid().to_string())
256 }
257}