sqlx_utils/traits/model.rs
1//! Model trait to define model specific methods
2
3use std::collections::{BTreeMap, HashMap};
4use std::hash::Hash;
5
6/// Trait for defining unique identification methods for database models.
7///
8/// The `Model` trait provides a standardized way to handle identification of database
9/// models, with built-in support for collections and wrapper types like [`Vec`], [`Option`],
10/// and [`Result`].
11///
12/// # Type Parameters
13///
14/// * [`Id`](Model::Id): The associated type representing the identifier type for the model.
15///
16/// # Examples
17///
18/// Basic implementation for a user model:
19/// ```rust
20/// # use sqlx_utils::traits::Model;
21/// struct User {
22/// id: i32,
23/// name: String,
24/// }
25///
26/// impl Model for User {
27/// type Id = i32;
28///
29/// fn get_id(&self) -> Option<Self::Id> {
30/// Some(self.id)
31/// }
32/// }
33/// ```
34///
35/// Working with collections of models:
36/// ```rust
37/// # use sqlx_utils::traits::Model;
38/// # struct User {
39/// # id: i32,
40/// # name: String,
41/// # }
42///
43/// # impl Model for User {
44/// # type Id = i32;
45/// #
46/// # fn get_id(&self) -> Option<Self::Id> {
47/// # Some(self.id)
48/// # }
49/// # }
50///
51/// let users = vec![
52/// User { id: 1, name: String::from("Alice") },
53/// User { id: 2, name: String::from("Bob") }
54/// ];
55///
56/// // Get IDs for all users in the vector
57/// let ids = users.get_id(); // Returns Some(vec![Some(1), Some(2)])
58/// ```
59///
60/// Handling optional models:
61/// ```rust
62/// # use sqlx_utils::traits::Model;
63/// # struct User {
64/// # id: i32,
65/// # name: String,
66/// # }
67///
68/// # impl Model for User {
69/// # type Id = i32;
70/// #
71/// # fn get_id(&self) -> Option<Self::Id> {
72/// # Some(self.id)
73/// # }
74/// # }
75/// let maybe_user: Option<User> = Some(User { id: 1, name: String::from("Alice") });
76/// let id = maybe_user.get_id(); // Returns Some(1)
77///
78/// let no_user: Option<User> = None;
79/// let id = no_user.get_id(); // Returns None
80/// ```
81///
82/// # Implementation Notes
83///
84/// The trait provides automatic implementations for:
85///
86/// * [`Vec<M>`]: Returns a vector of optional IDs
87/// * [`Option<M>`]: Propagates the inner model's ID
88/// * [`Result<M, E>`](Result): Returns the ID from Ok variants, None for Err
89///
90/// All implementations use `#[inline]` for optimal performance in tight loops
91/// or when working with large collections of models.
92#[diagnostic::on_unimplemented(
93 note = "Type `{Self}` does not implement the `Model` trait which is required for database operations",
94 label = "this type does not implement `Model`",
95 message = "`{Self}` must implement `Model` to define how to identify database records"
96)]
97pub trait Model: Send + Sync {
98 /// The type used for model identification
99 type Id: Send;
100
101 /// Returns the model's identifier if available
102 ///
103 /// # Returns
104 ///
105 /// * [`Some(Id)`](Some) - If the model has an identifier
106 /// * [`None`] - If the model has no identifier
107 fn get_id(&self) -> Option<Self::Id>;
108
109 fn has_id(&self) -> bool {
110 self.get_id().is_some()
111 }
112}
113
114impl<M> Model for Vec<M>
115where
116 M: Model + Send + Sync,
117{
118 type Id = Vec<Option<M::Id>>;
119
120 #[inline]
121 fn get_id(&self) -> Option<Self::Id> {
122 let mut ids = Vec::new();
123
124 for m in self {
125 ids.push(m.get_id())
126 }
127
128 Some(ids)
129 }
130}
131
132impl<M> Model for Option<M>
133where
134 M: Model + Send + Sync,
135{
136 type Id = M::Id;
137
138 #[inline]
139 fn get_id(&self) -> Option<Self::Id> {
140 if let Some(m) = self {
141 m.get_id()
142 } else {
143 None
144 }
145 }
146}
147
148impl<M, E> Model for Result<M, E>
149where
150 M: Model + Send + Sync,
151 E: Send + Sync,
152{
153 type Id = M::Id;
154
155 #[inline]
156 fn get_id(&self) -> Option<Self::Id> {
157 if let Ok(m) = self {
158 m.get_id()
159 } else {
160 None
161 }
162 }
163}
164
165impl<K, V> Model for HashMap<K, V>
166where
167 K: Eq + Hash + Clone + Send + Sync,
168 V: Model + Send + Sync,
169{
170 type Id = HashMap<K, Option<V::Id>>;
171
172 #[inline]
173 fn get_id(&self) -> Option<Self::Id> {
174 Some(self.iter().map(|(k, v)| (k.clone(), v.get_id())).collect())
175 }
176}
177
178impl<K, V> Model for BTreeMap<K, V>
179where
180 K: Ord + Clone + Send + Sync,
181 V: Model + Send + Sync,
182{
183 type Id = BTreeMap<K, Option<V::Id>>;
184
185 #[inline]
186 fn get_id(&self) -> Option<Self::Id> {
187 Some(self.iter().map(|(k, v)| (k.clone(), v.get_id())).collect())
188 }
189}