Skip to main content

rustorm_core/
model.rs

1use crate::error::OrmResult;
2use crate::query::QueryBuilder;
3use async_trait::async_trait;
4use sqlx::{PgPool, Row};
5
6/// Главный трейт, который реализует каждая модель.
7/// Генерируется `#[derive(Model)]`.
8#[async_trait]
9pub trait Model: Sized + Send + Sync + Unpin + 'static {
10    /// Имя таблицы в БД.
11    fn table_name() -> &'static str;
12
13    /// Имя первичного ключа.
14    fn primary_key() -> &'static str {
15        "id"
16    }
17
18    /// Список всех колонок таблицы (в порядке SELECT *).
19    fn columns() -> &'static [&'static str];
20
21    /// Создаёт QueryBuilder для данной модели.
22    fn query() -> QueryBuilder<Self> {
23        QueryBuilder::new(Self::table_name(), Self::primary_key())
24    }
25
26    /// `SELECT * FROM table WHERE pk = id`
27    async fn find(id: i64, pool: &PgPool) -> OrmResult<Option<Self>>
28    where
29        Self: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
30    {
31        let sql = format!(
32            "SELECT * FROM \"{}\" WHERE \"{}\" = $1",
33            Self::table_name(),
34            Self::primary_key(),
35        );
36        let row = sqlx::query_as::<_, Self>(&sql)
37            .bind(id)
38            .fetch_optional(pool)
39            .await
40            .map_err(crate::error::OrmError::from_sqlx)?;
41        Ok(row)
42    }
43
44    /// Как `find`, но возвращает `OrmError::NotFound` если запись не найдена.
45    async fn find_or_fail(id: i64, pool: &PgPool) -> OrmResult<Self>
46    where
47        Self: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
48    {
49        Self::find(id, pool)
50            .await?
51            .ok_or(crate::error::OrmError::NotFound)
52    }
53
54    /// Находит несколько записей по списку id.
55    async fn find_many(ids: &[i64], pool: &PgPool) -> OrmResult<Vec<Self>>
56    where
57        Self: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
58    {
59        if ids.is_empty() {
60            return Ok(vec![]);
61        }
62        let placeholders: Vec<String> = (1..=ids.len()).map(|i| format!("${}", i)).collect();
63        let sql = format!(
64            "SELECT * FROM \"{}\" WHERE \"{}\" IN ({})",
65            Self::table_name(),
66            Self::primary_key(),
67            placeholders.join(", "),
68        );
69        let mut q = sqlx::query_as::<_, Self>(&sql);
70        for id in ids {
71            q = q.bind(id);
72        }
73        let rows = q
74            .fetch_all(pool)
75            .await
76            .map_err(crate::error::OrmError::from_sqlx)?;
77        Ok(rows)
78    }
79
80    /// Все записи таблицы.
81    async fn all(pool: &PgPool) -> OrmResult<Vec<Self>>
82    where
83        Self: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
84    {
85        let sql = format!("SELECT * FROM \"{}\"", Self::table_name());
86        let rows = sqlx::query_as::<_, Self>(&sql)
87            .fetch_all(pool)
88            .await
89            .map_err(crate::error::OrmError::from_sqlx)?;
90        Ok(rows)
91    }
92
93    /// Первая запись (по PK ASC).
94    async fn first(pool: &PgPool) -> OrmResult<Option<Self>>
95    where
96        Self: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
97    {
98        let sql = format!(
99            "SELECT * FROM \"{}\" ORDER BY \"{}\" ASC LIMIT 1",
100            Self::table_name(),
101            Self::primary_key(),
102        );
103        let row = sqlx::query_as::<_, Self>(&sql)
104            .fetch_optional(pool)
105            .await
106            .map_err(crate::error::OrmError::from_sqlx)?;
107        Ok(row)
108    }
109
110    /// Последняя запись (по PK DESC).
111    async fn last(pool: &PgPool) -> OrmResult<Option<Self>>
112    where
113        Self: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
114    {
115        let sql = format!(
116            "SELECT * FROM \"{}\" ORDER BY \"{}\" DESC LIMIT 1",
117            Self::table_name(),
118            Self::primary_key(),
119        );
120        let row = sqlx::query_as::<_, Self>(&sql)
121            .fetch_optional(pool)
122            .await
123            .map_err(crate::error::OrmError::from_sqlx)?;
124        Ok(row)
125    }
126
127    /// Количество записей.
128    async fn count(pool: &PgPool) -> OrmResult<i64> {
129        let sql = format!("SELECT COUNT(*) FROM \"{}\"", Self::table_name());
130        let row: (i64,) = sqlx::query_as(&sql)
131            .fetch_one(pool)
132            .await
133            .map_err(crate::error::OrmError::from_sqlx)?;
134        Ok(row.0)
135    }
136
137    /// Есть ли хоть одна запись.
138    async fn any(pool: &PgPool) -> OrmResult<bool> {
139        Ok(Self::count(pool).await? > 0)
140    }
141
142    /// Существует ли запись с данным id.
143    async fn exists(id: i64, pool: &PgPool) -> OrmResult<bool> {
144        let sql = format!(
145            "SELECT EXISTS(SELECT 1 FROM \"{}\" WHERE \"{}\" = $1)",
146            Self::table_name(),
147            Self::primary_key(),
148        );
149        let row: (bool,) = sqlx::query_as(&sql)
150            .bind(id)
151            .fetch_one(pool)
152            .await
153            .map_err(crate::error::OrmError::from_sqlx)?;
154        Ok(row.0)
155    }
156}