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