1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
7use ulid::Ulid;
8
9pub mod prelude;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum UlidIdError {
13 InvalidFormat(String),
14}
15
16impl fmt::Display for UlidIdError {
17 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
18 match self {
19 Self::InvalidFormat(message) => formatter.write_str(message),
20 }
21 }
22}
23
24impl std::error::Error for UlidIdError {}
25
26#[must_use]
27pub fn is_ulid(input: &str) -> bool {
28 input.parse::<Ulid>().is_ok()
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Hash)]
32pub struct UlidId(Ulid);
33
34impl UlidId {
35 pub fn parse(input: &str) -> Result<Self, UlidIdError> {
41 input
42 .parse::<Ulid>()
43 .map(Self)
44 .map_err(|error| UlidIdError::InvalidFormat(error.to_string()))
45 }
46
47 #[must_use]
48 pub const fn from_ulid(ulid: Ulid) -> Self {
49 Self(ulid)
50 }
51
52 #[must_use]
53 pub const fn as_ulid(&self) -> &Ulid {
54 &self.0
55 }
56
57 #[must_use]
58 pub fn to_canonical(&self) -> String {
59 self.0.to_string()
60 }
61}
62
63impl fmt::Display for UlidId {
64 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
65 formatter.write_str(&self.to_canonical())
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::{UlidId, is_ulid};
72
73 #[test]
74 fn ulid_validation_and_formatting_work() -> Result<(), Box<dyn std::error::Error>> {
75 let value = UlidId::parse("01ARZ3NDEKTSV4RRFFQ69G5FAV")?;
76 assert!(is_ulid(value.to_canonical().as_str()));
77 assert_eq!(value.to_canonical(), "01ARZ3NDEKTSV4RRFFQ69G5FAV");
78 Ok(())
79 }
80}