kit_rs/database/model.rs
1//! Model traits for Kit ORM
2//!
3//! Provides Laravel-like active record pattern over SeaORM entities.
4
5use async_trait::async_trait;
6use sea_orm::{
7 ActiveModelBehavior, ActiveModelTrait, EntityTrait, IntoActiveModel, ModelTrait,
8 PaginatorTrait, PrimaryKeyTrait, TryIntoModel,
9};
10
11use crate::database::DB;
12use crate::error::FrameworkError;
13
14/// Trait providing Laravel-like read operations on SeaORM entities
15///
16/// Implement this trait on your SeaORM Entity to get convenient static methods
17/// for querying records.
18///
19/// # Example
20///
21/// ```rust,ignore
22/// use kit::database::Model;
23/// use sea_orm::entity::prelude::*;
24///
25/// #[derive(Clone, Debug, DeriveEntityModel)]
26/// #[sea_orm(table_name = "users")]
27/// pub struct Model {
28/// #[sea_orm(primary_key)]
29/// pub id: i32,
30/// pub name: String,
31/// pub email: String,
32/// }
33///
34/// #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
35/// pub enum Relation {}
36///
37/// impl ActiveModelBehavior for ActiveModel {}
38///
39/// // Add Kit's Model trait
40/// impl kit::database::Model for Entity {}
41///
42/// // Now you can use:
43/// let users = Entity::all().await?;
44/// let user = Entity::find_by_pk(1).await?;
45/// ```
46#[async_trait]
47pub trait Model: EntityTrait + Sized
48where
49 Self::Model: ModelTrait<Entity = Self> + Send + Sync,
50{
51 /// Find all records
52 ///
53 /// # Example
54 /// ```rust,ignore
55 /// let users = user::Entity::all().await?;
56 /// ```
57 async fn all() -> Result<Vec<Self::Model>, FrameworkError> {
58 let db = DB::connection()?;
59 Self::find()
60 .all(db.inner())
61 .await
62 .map_err(|e| FrameworkError::database(e.to_string()))
63 }
64
65 /// Find a record by primary key (generic version)
66 ///
67 /// # Example
68 /// ```rust,ignore
69 /// let user = user::Entity::find_by_pk(1).await?;
70 /// ```
71 async fn find_by_pk<K>(id: K) -> Result<Option<Self::Model>, FrameworkError>
72 where
73 K: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType> + Send,
74 {
75 let db = DB::connection()?;
76 Self::find_by_id(id)
77 .one(db.inner())
78 .await
79 .map_err(|e| FrameworkError::database(e.to_string()))
80 }
81
82 /// Find a record by primary key or return an error
83 ///
84 /// # Example
85 /// ```rust,ignore
86 /// let user = user::Entity::find_or_fail(1).await?;
87 /// ```
88 async fn find_or_fail<K>(id: K) -> Result<Self::Model, FrameworkError>
89 where
90 K: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType> + Send + std::fmt::Debug + Copy,
91 {
92 Self::find_by_pk(id).await?.ok_or_else(|| {
93 FrameworkError::database(format!(
94 "{} with id {:?} not found",
95 std::any::type_name::<Self>(),
96 id
97 ))
98 })
99 }
100
101 /// Count all records
102 ///
103 /// # Example
104 /// ```rust,ignore
105 /// let count = user::Entity::count_all().await?;
106 /// ```
107 async fn count_all() -> Result<u64, FrameworkError> {
108 let db = DB::connection()?;
109 Self::find()
110 .count(db.inner())
111 .await
112 .map_err(|e| FrameworkError::database(e.to_string()))
113 }
114
115 /// Check if any records exist
116 ///
117 /// # Example
118 /// ```rust,ignore
119 /// if user::Entity::exists_any().await? {
120 /// println!("Users exist!");
121 /// }
122 /// ```
123 async fn exists_any() -> Result<bool, FrameworkError> {
124 Ok(Self::count_all().await? > 0)
125 }
126
127 /// Get the first record
128 ///
129 /// # Example
130 /// ```rust,ignore
131 /// let first_user = user::Entity::first().await?;
132 /// ```
133 async fn first() -> Result<Option<Self::Model>, FrameworkError> {
134 let db = DB::connection()?;
135 Self::find()
136 .one(db.inner())
137 .await
138 .map_err(|e| FrameworkError::database(e.to_string()))
139 }
140}
141
142/// Trait providing Laravel-like write operations on SeaORM entities
143///
144/// Implement this trait alongside `Model` to get insert/update/delete methods.
145///
146/// # Example
147///
148/// ```rust,ignore
149/// use kit::database::{Model, ModelMut};
150/// use sea_orm::Set;
151///
152/// // Implement both traits
153/// impl kit::database::Model for Entity {}
154/// impl kit::database::ModelMut for Entity {}
155///
156/// // Insert a new record
157/// let new_user = user::ActiveModel {
158/// name: Set("John".to_string()),
159/// email: Set("john@example.com".to_string()),
160/// ..Default::default()
161/// };
162/// let user = user::Entity::insert_one(new_user).await?;
163///
164/// // Delete by ID
165/// user::Entity::delete_by_pk(user.id).await?;
166/// ```
167#[async_trait]
168pub trait ModelMut: Model
169where
170 Self::Model: ModelTrait<Entity = Self> + IntoActiveModel<Self::ActiveModel> + Send + Sync,
171 Self::ActiveModel: ActiveModelTrait<Entity = Self> + ActiveModelBehavior + Send,
172{
173 /// Insert a new record
174 ///
175 /// # Example
176 /// ```rust,ignore
177 /// let new_user = user::ActiveModel {
178 /// name: Set("John".to_string()),
179 /// email: Set("john@example.com".to_string()),
180 /// ..Default::default()
181 /// };
182 /// let user = user::Entity::insert_one(new_user).await?;
183 /// ```
184 async fn insert_one(model: Self::ActiveModel) -> Result<Self::Model, FrameworkError> {
185 let db = DB::connection()?;
186 model
187 .insert(db.inner())
188 .await
189 .map_err(|e| FrameworkError::database(e.to_string()))
190 }
191
192 /// Update an existing record
193 ///
194 /// # Example
195 /// ```rust,ignore
196 /// let mut user: user::ActiveModel = user.into();
197 /// user.name = Set("Updated Name".to_string());
198 /// let updated = user::Entity::update_one(user).await?;
199 /// ```
200 async fn update_one(model: Self::ActiveModel) -> Result<Self::Model, FrameworkError> {
201 let db = DB::connection()?;
202 model
203 .update(db.inner())
204 .await
205 .map_err(|e| FrameworkError::database(e.to_string()))
206 }
207
208 /// Delete a record by primary key
209 ///
210 /// # Example
211 /// ```rust,ignore
212 /// let rows_deleted = user::Entity::delete_by_pk(1).await?;
213 /// ```
214 async fn delete_by_pk<K>(id: K) -> Result<u64, FrameworkError>
215 where
216 K: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType> + Send,
217 {
218 let db = DB::connection()?;
219 let result = Self::delete_by_id(id)
220 .exec(db.inner())
221 .await
222 .map_err(|e| FrameworkError::database(e.to_string()))?;
223 Ok(result.rows_affected)
224 }
225
226 /// Save a model (insert or update based on whether primary key is set)
227 ///
228 /// # Example
229 /// ```rust,ignore
230 /// let user = user::ActiveModel {
231 /// name: Set("John".to_string()),
232 /// ..Default::default()
233 /// };
234 /// let saved = user::Entity::save_one(user).await?;
235 /// ```
236 async fn save_one(model: Self::ActiveModel) -> Result<Self::Model, FrameworkError>
237 where
238 Self::ActiveModel: TryIntoModel<Self::Model>,
239 {
240 let db = DB::connection()?;
241 let saved = model
242 .save(db.inner())
243 .await
244 .map_err(|e| FrameworkError::database(e.to_string()))?;
245 saved
246 .try_into_model()
247 .map_err(|e| FrameworkError::database(e.to_string()))
248 }
249}