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}