1use converter::BaseConverter;
135use error::{CustomAlphabetError, ErrorKind, InvalidShortUuid};
136
137pub mod converter;
139mod error;
140mod fmt;
141
142mod macros;
143use uuid;
144
145pub const FLICKR_BASE_58: &str = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
146
147pub const COOKIE_BASE_90: &str =
148 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&'()*+-./:<=>?@[]^_`{|}~";
149
150type UuidError = uuid::Error;
151
152pub type Bytes = Vec<u8>;
155
156#[derive(Clone, Debug, Eq, PartialEq, Hash)]
158pub struct ShortUuid(Bytes);
159
160#[derive(Clone, Debug, Eq, PartialEq, Hash)]
162pub struct ShortUuidCustom(Bytes);
163
164pub type CustomAlphabet = &'static str;
166
167pub struct CustomTranslator(BaseConverter);
169
170impl CustomTranslator {
171 pub fn new(custom_alphabet: CustomAlphabet) -> Result<Self, CustomAlphabetError> {
173 let converter = BaseConverter::new_custom(custom_alphabet)?;
174 Ok(Self(converter))
175 }
176
177 fn as_slice(&self) -> &BaseConverter {
178 &self.0
179 }
180}
181
182impl From<ShortUuid> for ShortUuidCustom {
183 fn from(short_uuid: ShortUuid) -> Self {
184 ShortUuidCustom(short_uuid.0)
185 }
186}
187
188#[cfg(feature = "serde")]
190impl serde::Serialize for ShortUuid {
191 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
192 where
193 S: serde::Serializer,
194 {
195 let string = String::from_utf8(self.0.clone())
196 .map_err(|e| serde::ser::Error::custom(e.to_string()))?;
197 serializer.serialize_str(&string)
198 }
199}
200
201#[cfg(feature = "serde")]
203impl<'de> serde::Deserialize<'de> for ShortUuid {
204 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
205 where
206 D: serde::Deserializer<'de>,
207 {
208 let string = String::deserialize(deserializer)?;
209 Ok(ShortUuid(string.into_bytes()))
210 }
211}
212
213#[cfg(feature = "serde")]
215impl serde::Serialize for ShortUuidCustom {
216 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
217 where
218 S: serde::Serializer,
219 {
220 let string = String::from_utf8(self.0.clone())
221 .map_err(|e| serde::ser::Error::custom(e.to_string()))?;
222 serializer.serialize_str(&string)
223 }
224}
225
226#[cfg(feature = "serde")]
228impl<'de> serde::Deserialize<'de> for ShortUuidCustom {
229 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
230 where
231 D: serde::Deserializer<'de>,
232 {
233 let string = String::deserialize(deserializer)?;
234 Ok(ShortUuidCustom(string.into_bytes()))
235 }
236}
237
238impl ShortUuid {
239 pub fn generate() -> ShortUuid {
241 generate_short(None)
242 }
243
244 pub fn from_uuid_str(uuid_string: &str) -> Result<ShortUuid, UuidError> {
246 let parsed = uuid::Uuid::parse_str(uuid_string)?;
248
249 let cleaned = parsed.to_string().to_lowercase().replace("-", "");
250
251 let converter = BaseConverter::default();
252
253 let result = converter.convert(&cleaned).unwrap();
255
256 Ok(ShortUuid(result))
257 }
258
259 pub fn from_uuid(uuid: &uuid::Uuid) -> ShortUuid {
261 let uuid_string = uuid.to_string();
262
263 let cleaned = uuid_string.to_lowercase().replace("-", "");
264
265 let converter = BaseConverter::default();
266
267 let result = converter.convert(&cleaned).unwrap();
269
270 ShortUuid(result)
271 }
272
273 pub fn to_uuid(self) -> uuid::Uuid {
275 let to_hex_converter = BaseConverter::default();
277
278 let result = to_hex_converter.convert_to_hex(&self.0).unwrap();
280
281 format_uuid(result)
283 }
284
285 pub fn parse_str(short_uuid_str: &str) -> Result<Self, InvalidShortUuid> {
287 let expected_len = 22;
288
289 if short_uuid_str.len() != expected_len {
290 return Err(InvalidShortUuid);
291 };
292
293 let byte_vector: Vec<u8> = short_uuid_str.as_bytes().to_vec();
294
295 let to_hex_converter = BaseConverter::default();
296
297 let result = to_hex_converter
299 .convert_to_hex(&byte_vector)
300 .map_err(|_| InvalidShortUuid)?;
301
302 uuid::Uuid::try_parse(&result).map_err(|_| InvalidShortUuid)?;
304
305 Ok(Self(byte_vector))
306 }
307
308 pub fn as_slice(&self) -> &[u8] {
309 &self.0
310 }
311}
312
313impl ShortUuidCustom {
314 pub fn generate(translator: &CustomTranslator) -> Self {
316 let generated = generate_short(Some(&translator.as_slice()));
318 let short_custom: ShortUuidCustom = generated.into();
319
320 short_custom
321 }
322
323 pub fn from_uuid(uuid: &uuid::Uuid, translator: &CustomTranslator) -> Self {
325 let uuid_string = uuid.to_string();
326
327 let cleaned = uuid_string.to_lowercase().replace("-", "");
328
329 let result = translator.as_slice().convert(&cleaned).unwrap();
331
332 Self(result)
333 }
334
335 pub fn from_uuid_str(
337 uuid_string: &str,
338 translator: &CustomTranslator,
339 ) -> Result<Self, ErrorKind> {
340 let parsed = uuid::Uuid::parse_str(uuid_string).map_err(|e| ErrorKind::UuidError(e))?;
342
343 let cleaned = parsed.to_string().to_lowercase().replace("-", "");
344
345 let result = translator.as_slice().convert(&cleaned).unwrap();
347
348 Ok(Self(result))
349 }
350
351 pub fn to_uuid(self, translator: &CustomTranslator) -> Result<uuid::Uuid, CustomAlphabetError> {
353 let result = translator
356 .as_slice()
357 .convert_to_hex(&self.as_slice())
358 .unwrap();
359
360 let uuid_value = format_uuid(result);
362
363 Ok(uuid_value)
364 }
365
366 pub fn parse_str(
368 short_uuid_str: &str,
369 translator: &CustomTranslator,
370 ) -> Result<Self, InvalidShortUuid> {
371 let byte_vector: Vec<u8> = short_uuid_str.as_bytes().to_vec();
372
373 let result_string = translator
374 .as_slice()
375 .convert_to_hex(&byte_vector)
376 .map_err(|_| InvalidShortUuid)?;
377
378 uuid::Uuid::try_parse(&result_string).map_err(|_| InvalidShortUuid)?;
380
381 Ok(Self(byte_vector))
382 }
383
384 pub fn as_slice(&self) -> &[u8] {
385 &self.0
386 }
387}
388
389fn generate_short(base_converter: Option<&BaseConverter>) -> ShortUuid {
390 let uuid_string = uuid::Uuid::new_v4().to_string();
392
393 let cleaned = uuid_string.to_lowercase().replace("-", "");
395
396 let result = base_converter
398 .unwrap_or(&BaseConverter::default())
399 .convert(&cleaned)
400 .unwrap();
401
402 ShortUuid(result)
403}
404
405fn format_uuid(value: String) -> uuid::Uuid {
406 let formatted_uuid = format!(
407 "{}-{}-{}-{}-{}",
408 &value[0..8],
409 &value[8..12],
410 &value[12..16],
411 &value[16..20],
412 &value[20..32]
413 );
414
415 let uuid = uuid::Uuid::parse_str(&formatted_uuid).unwrap();
417
418 return uuid;
419}
420
421impl From<uuid::Uuid> for ShortUuid {
422 fn from(uuid: uuid::Uuid) -> ShortUuid {
423 ShortUuid::from_uuid(&uuid)
424 }
425}
426
427impl From<ShortUuid> for uuid::Uuid {
428 fn from(short_uuid: ShortUuid) -> uuid::Uuid {
429 short_uuid.to_uuid()
430 }
431}