crdb_core/
ids.rs

1use ulid::Ulid;
2
3macro_rules! impl_id {
4    ($type:ident) => {
5        #[derive(
6            Clone,
7            Copy,
8            Eq,
9            Hash,
10            Ord,
11            PartialEq,
12            PartialOrd,
13            educe::Educe,
14            serde::Deserialize,
15            serde::Serialize,
16        )]
17        #[educe(Debug)]
18        pub struct $type(#[educe(Debug(method(std::fmt::Display::fmt)))] pub Ulid);
19
20        #[allow(dead_code)]
21        impl $type {
22            pub fn now() -> Self {
23                Self(Ulid::new())
24            }
25
26            #[cfg(feature = "uuid")]
27            pub fn to_uuid(self) -> uuid::Uuid {
28                uuid::Uuid::from_bytes(self.0.to_bytes())
29            }
30
31            #[cfg(feature = "uuid")]
32            pub fn from_uuid(id: uuid::Uuid) -> Self {
33                Self(Ulid::from_bytes(*id.as_bytes()))
34            }
35
36            #[cfg(feature = "indexed-db")]
37            pub fn to_js_string(&self) -> web_sys::js_sys::JsString {
38                web_sys::js_sys::JsString::from(format!("{}", self.0))
39            }
40
41            pub fn from_u128(v: u128) -> Self {
42                Self(Ulid::from_bytes(v.to_be_bytes()))
43            }
44
45            pub fn as_u128(&self) -> u128 {
46                u128::from_be_bytes(self.0.to_bytes())
47            }
48        }
49
50        #[cfg(feature = "sqlx-postgres")]
51        impl<'q> sqlx::encode::Encode<'q, sqlx::Postgres> for $type {
52            fn encode_by_ref(&self, buf: &mut sqlx::postgres::PgArgumentBuffer) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Sync + Send>> {
53                <uuid::Uuid as sqlx::encode::Encode<'q, sqlx::Postgres>>::encode_by_ref(&self.to_uuid(), buf)
54            }
55            fn encode(self, buf: &mut sqlx::postgres::PgArgumentBuffer) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Sync + Send>> {
56                <uuid::Uuid as sqlx::encode::Encode<'q, sqlx::Postgres>>::encode(self.to_uuid(), buf)
57            }
58            fn produces(&self) -> Option<sqlx::postgres::PgTypeInfo> {
59                <uuid::Uuid as sqlx::encode::Encode<'q, sqlx::Postgres>>::produces(&self.to_uuid())
60            }
61            fn size_hint(&self) -> usize {
62                <uuid::Uuid as sqlx::encode::Encode<'q, sqlx::Postgres>>::size_hint(&self.to_uuid())
63            }
64        }
65
66        #[cfg(feature = "sqlx-postgres")]
67        impl sqlx::Type<sqlx::Postgres> for $type {
68            fn type_info() -> sqlx::postgres::PgTypeInfo {
69                <uuid::Uuid as sqlx::Type<sqlx::Postgres>>::type_info()
70            }
71            fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
72                <uuid::Uuid as sqlx::Type<sqlx::Postgres>>::compatible(ty)
73            }
74        }
75
76        #[cfg(feature = "sqlx-postgres")]
77        impl sqlx::postgres::PgHasArrayType for $type {
78            fn array_type_info() -> sqlx::postgres::PgTypeInfo {
79                <uuid::Uuid as sqlx::postgres::PgHasArrayType>::array_type_info()
80            }
81            fn array_compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
82                <uuid::Uuid as sqlx::postgres::PgHasArrayType>::array_compatible(ty)
83            }
84        }
85
86        #[cfg(feature = "sqlx-sqlite")]
87        impl<'q> sqlx::encode::Encode<'q, sqlx::Sqlite> for $type {
88            fn encode_by_ref(&self, buf: &mut Vec<sqlx::sqlite::SqliteArgumentValue<'q>>) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Sync + Send>> {
89                <uuid::Uuid as sqlx::encode::Encode<'q, sqlx::Sqlite>>::encode_by_ref(&self.to_uuid(), buf)
90            }
91            fn encode(self, buf: &mut Vec<sqlx::sqlite::SqliteArgumentValue<'q>>) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Sync + Send>> {
92                <uuid::Uuid as sqlx::encode::Encode<'q, sqlx::Sqlite>>::encode(self.to_uuid(), buf)
93            }
94            fn produces(&self) -> Option<sqlx::sqlite::SqliteTypeInfo> {
95                <uuid::Uuid as sqlx::encode::Encode<'q, sqlx::Sqlite>>::produces(&self.to_uuid())
96            }
97            fn size_hint(&self) -> usize {
98                <uuid::Uuid as sqlx::encode::Encode<'q, sqlx::Sqlite>>::size_hint(&self.to_uuid())
99            }
100        }
101
102        #[cfg(feature = "sqlx-sqlite")]
103        impl sqlx::Type<sqlx::Sqlite> for $type {
104            fn type_info() -> sqlx::sqlite::SqliteTypeInfo {
105                <uuid::Uuid as sqlx::Type<sqlx::Sqlite>>::type_info()
106            }
107            fn compatible(ty: &sqlx::sqlite::SqliteTypeInfo) -> bool {
108                <uuid::Uuid as sqlx::Type<sqlx::Sqlite>>::compatible(ty)
109            }
110        }
111
112        #[cfg(feature = "arbitrary")]
113        impl<'a> arbitrary::Arbitrary<'a> for $type {
114            fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
115                Ok(Self(Ulid::from_bytes(u.arbitrary()?)))
116            }
117        }
118
119        deepsize::known_deep_size!(0; $type); // These types does not allocate
120    };
121}
122
123impl_id!(ObjectId);
124impl_id!(EventId);
125impl_id!(TypeId);
126impl_id!(BinPtr);
127impl_id!(QueryId);
128impl_id!(User);
129impl_id!(SessionRef);
130impl_id!(SessionToken);
131impl_id!(Updatedness);
132
133impl SessionToken {
134    #[cfg(feature = "server")]
135    pub fn new() -> SessionToken {
136        use rand::Rng;
137        SessionToken(ulid::Ulid::from_bytes(rand::rng().random()))
138    }
139}
140
141#[cfg(feature = "server")]
142impl Default for SessionToken {
143    fn default() -> SessionToken {
144        SessionToken::new()
145    }
146}