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}