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);