Skip to main content

post_archiver/
id.rs

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