1use std::fmt::Debug;
2
3pub mod exports {
4 pub use {diesel, serde, ulid, uuid};
5}
6
7#[macro_export]
8macro_rules! impl_id {
9 ($vis:vis $type:ident, $prefix:literal) => {
10 #[derive(
11 $crate::exports::diesel::deserialize::FromSqlRow,
12 $crate::exports::diesel::expression::AsExpression,
13 Debug,
14 PartialEq,
15 Eq,
16 Hash,
17 Clone,
18 Copy,
19 Default,
20 )]
21 #[diesel(sql_type = $crate::exports::diesel::sql_types::Uuid)]
22 $vis struct $type($crate::exports::ulid::Ulid);
23
24 impl ::std::convert::From<$type> for $crate::exports::ulid::Ulid {
25 fn from(value: $type) -> Self {
26 value.0
27 }
28 }
29
30 impl ::std::convert::From<$crate::exports::ulid::Ulid> for $type {
31 fn from(value: $crate::exports::ulid::Ulid) -> Self {
32 Self(value)
33 }
34 }
35
36 impl ::std::convert::From<$crate::exports::uuid::Uuid> for $type {
37 fn from(value: $crate::exports::uuid::Uuid) -> Self {
38 Self($crate::exports::ulid::Ulid::from(value))
39 }
40 }
41
42 impl $crate::exports::diesel::deserialize::FromSql<$crate::exports::diesel::sql_types::Uuid, $crate::exports::diesel::pg::Pg> for $type {
43 fn from_sql(bytes: $crate::exports::diesel::pg::PgValue<'_>) -> $crate::exports::diesel::deserialize::Result<Self> {
44 let uuid: $crate::exports::uuid::Uuid = $crate::exports::diesel::deserialize::FromSql::from_sql(bytes)?;
45
46 Ok(Self($crate::exports::ulid::Ulid::from(uuid)))
47 }
48 }
49
50 impl $crate::exports::diesel::serialize::ToSql<$crate::exports::diesel::sql_types::Uuid, $crate::exports::diesel::pg::Pg> for $type {
51 fn to_sql<'b>(&'b self, out: &mut $crate::exports::diesel::serialize::Output<'b, '_, $crate::exports::diesel::pg::Pg>) -> $crate::exports::diesel::serialize::Result {
52 ::std::io::Write::write_all(out, &self.0.to_bytes())
53 .map(|_| $crate::exports::diesel::serialize::IsNull::No)
54 .map_err(Into::into)
55 }
56 }
57
58 impl $type {
59 $vis const PREFIX: &'static str = $prefix;
60
61 $vis fn new() -> Self {
62 Self($crate::exports::ulid::Ulid::new())
63 }
64
65 $vis fn ulid(&self) -> $crate::exports::ulid::Ulid {
66 self.0
67 }
68
69 $vis fn from_ulid(ulid: $crate::exports::ulid::Ulid) -> Self {
70 Self(ulid)
71 }
72
73 $vis fn datetime(&self) -> ::std::time::SystemTime {
74 self.0.datetime()
75 }
76 }
77
78 impl ::std::str::FromStr for $type {
79 type Err = $crate::IdParseError;
80
81 fn from_str(s: &str) -> Result<Self, Self::Err> {
82 let id = s.strip_prefix(Self::PREFIX).ok_or($crate::IdParseError::PrefixMismatch(Self::PREFIX))?;
83 let id = $crate::exports::ulid::Ulid::from_str(id)?;
84 Ok(Self::from_ulid(id))
85 }
86 }
87
88 impl ::std::fmt::Display for $type {
89 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
90 write!(f, "{}{}", Self::PREFIX, self.0)
91 }
92 }
93
94 impl ::serde::Serialize for $type {
95 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
96 where
97 S: ::serde::Serializer,
98 {
99 serializer.serialize_str(&self.to_string())
100 }
101 }
102
103 impl<'de> ::serde::Deserialize<'de> for $type {
104 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
105 where
106 D: ::serde::Deserializer<'de>,
107 {
108 let s = ::std::string::String::deserialize(deserializer)?;
109 s.parse().map_err(serde::de::Error::custom)
110 }
111 }
112 };
113}
114
115#[derive(Debug, thiserror::Error)]
116pub enum IdParseError {
117 #[error("id doesnt have prefix {0}")]
118 PrefixMismatch(&'static str),
119 #[error("invalid ulid: {0}")]
120 Ulid(#[from] ulid::DecodeError),
121}