Skip to main content

post_archiver/
id.rs

1use core::fmt;
2use serde::{Deserialize, Serialize};
3#[cfg(feature = "typescript")]
4use ts_rs::TS;
5
6pub trait HasId {
7    type Id: std::hash::Hash + Eq + Clone;
8    fn id(&self) -> Self::Id;
9}
10
11/// Defines a strongly-typed numeric identifier type
12///
13/// # Safety
14/// - The value must never be negative
15/// - The maximum value is constrained by u32::MAX
16///
17/// # Examples
18/// ```rust
19/// use post_archiver::{AuthorId, PostId};
20///
21/// // Create an author ID
22/// let author_id = AuthorId::new(1);
23/// assert_eq!(author_id.raw(), 1);
24///
25/// // Convert from usize
26/// let id_from_usize = AuthorId::from(2_usize);
27/// assert_eq!(id_from_usize.to_string(), "2");
28///
29/// // Type safety demonstration
30/// let post_id = PostId::new(1);
31///
32/// // This will not compile:
33/// // let _: PostId = author_id;
34/// ```
35macro_rules! define_id {
36    ($(#[$meta:meta])*,$table:ident : $name:ident) => {
37        #[cfg_attr(feature = "typescript", derive(TS))]
38        #[cfg_attr(feature = "typescript", ts(export))]
39        #[derive(Deserialize, Serialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
40        pub struct $name(pub u32);
41
42        impl core::ops::Deref for $name {
43            type Target = u32;
44            fn deref(&self) -> &Self::Target {
45                &self.0
46            }
47        }
48
49        impl core::ops::DerefMut for $name {
50            fn deref_mut(&mut self) -> &mut Self::Target {
51                &mut self.0
52            }
53        }
54
55        impl From<u32> for $name {
56            fn from(f: u32) -> Self {
57                Self(f)
58            }
59        }
60
61        impl From<$name> for u32 {
62            fn from(t: $name) -> Self {
63                t.0
64            }
65        }
66
67        impl $name {
68            pub fn new(id: u32) -> Self {
69                Self(id)
70            }
71            /// get the raw value of the id
72            pub fn raw(&self) -> u32 {
73                self.0
74            }
75        }
76
77        impl From<usize> for $name {
78            fn from(id: usize) -> Self {
79                Self(id as u32)
80            }
81        }
82
83        impl From<$name> for usize {
84            fn from(id: $name) -> usize {
85                id.0 as usize
86            }
87        }
88
89        impl AsRef<u32> for $name {
90            fn as_ref(&self) -> &u32 {
91                &self.0
92            }
93        }
94
95        impl fmt::Display for $name {
96            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97                write!(f, "{}", self.0)
98            }
99        }
100
101        #[cfg(feature = "utils")]
102        impl rusqlite::types::FromSql for $name {
103            fn column_result(
104                value: rusqlite::types::ValueRef<'_>,
105            ) -> rusqlite::types::FromSqlResult<Self> {
106                Ok(Self(value.as_i64()? as u32))
107            }
108        }
109
110        #[cfg(feature = "utils")]
111        impl rusqlite::types::ToSql for $name {
112            fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
113                Ok(rusqlite::types::ToSqlOutput::Owned(
114                    rusqlite::types::Value::Integer(self.0 as i64),
115                ))
116            }
117        }
118
119        #[cfg(feature = "utils")]
120        impl crate::id::HasId for crate::$table {
121            type Id = $name;
122            fn id(&self) -> $name {
123                self.id.clone()
124            }
125        }
126    };
127}
128
129define_id!(
130/// Unique identifier for an author in the system
131///
132/// # Safety
133/// - The wrapped value must be a valid u32
134/// - Must maintain referential integrity when used as a foreign key
135,Author: AuthorId);
136
137define_id!(
138/// Unique identifier for a post in the system
139///
140/// # Safety
141/// - The wrapped value must be a valid u32
142/// - Must maintain referential integrity when used as a foreign key
143,Post: PostId);
144
145define_id!(
146/// Unique identifier for a file metadata entry in the system
147///
148/// # Safety
149/// - The wrapped value must be a valid u32
150/// - Must maintain referential integrity when used as a foreign key
151,FileMeta: FileMetaId);
152
153define_id!(
154/// Unique identifier for a post tag in the system
155///
156/// # Safety
157/// - The wrapped value must be a valid u32
158/// - Must maintain referential integrity when used as a foreign key
159,Tag: TagId);
160
161define_id!(
162/// Unique identifier for a platform in the system
163///
164/// # Safety
165/// - The wrapped value must be a valid u32
166/// - Must maintain referential integrity when used as a foreign key
167,Platform: PlatformId);
168
169define_id!(
170/// Unique identifier for a collection in the system
171///
172/// # Safety
173/// - The wrapped value must be a valid u32
174/// - Must maintain referential integrity when used as a foreign key
175,Collection: CollectionId);