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