articles_rs/databases/
images_repository.rs1use async_trait::async_trait;
7use chrono::{DateTime, Utc};
8use sqlx::{
9 postgres::{PgPool, PgRow},
10 Error as SqlxError, FromRow, Row,
11};
12use std::collections::HashMap;
13use uuid::Uuid;
14
15use super::postgres_repository::PostgresRepository;
16
17#[derive(Debug)]
19pub struct DbArticleImage {
20 pub id: Option<Uuid>,
21 pub article_id: Uuid,
22 pub image_path: String,
23 pub visible: bool,
24 pub created_at: DateTime<Utc>,
25}
26
27impl<'r> FromRow<'r, PgRow> for DbArticleImage {
29 fn from_row(row: &'r PgRow) -> Result<Self, SqlxError> {
30 let row_id = row.try_get("id").ok();
31 Ok(Self {
32 id: row_id,
33 article_id: row.get("article_id"),
34 image_path: row.get("image_path"),
35 visible: row.get("visible"),
36 created_at: row
37 .try_get::<DateTime<Utc>, _>("created_at")
38 .unwrap_or_else(|_| {
39 let naive_date: chrono::NaiveDateTime = row.try_get("created_at").unwrap();
40 DateTime::<Utc>::from_naive_utc_and_offset(naive_date, Utc)
41 }),
42 })
43 }
44}
45
46pub struct ImageRepository {
48 pool: PgPool,
49}
50
51impl ImageRepository {
52 pub fn new(pool: PgPool) -> Self {
57 Self { pool }
58 }
59}
60
61#[async_trait]
63impl PostgresRepository for ImageRepository {
64 type Error = SqlxError;
65 type Item = DbArticleImage;
66
67 async fn find_by_id(&self, id: Uuid) -> Result<Option<Self::Item>, Self::Error> {
68 let row = sqlx::query_as::<_, DbArticleImage>(
69 r#"
70 SELECT id, article_id, image_path, visible, created_at
71 FROM article_images
72 WHERE id = $1
73 "#,
74 )
75 .bind(id)
76 .fetch_optional(&self.pool)
77 .await?;
78
79 Ok(row)
80 }
81
82 async fn find_by_name(&self, _name: String) -> Result<Option<Self::Item>, Self::Error> {
83 Err(SqlxError::RowNotFound)
84 }
85
86 async fn list(
87 &self,
88 filters: Option<HashMap<String, String>>,
89 ) -> Result<Vec<Self::Item>, Self::Error> {
90 let mut query = String::from(
91 "SELECT id, article_id, image_path, visible, created_at FROM article_images WHERE 1=1",
92 );
93 let mut params = vec![];
94 let mut param_count = 1;
95
96 if let Some(filters) = filters {
97 for (key, value) in filters {
98 if key == "id" || key == "article_id" {
99 query.push_str(&format!(" AND {} = ${}::uuid", key, param_count));
101 } else {
102 query.push_str(&format!(" AND {} = ${}", key, param_count));
103 }
104 params.push(value);
105 param_count += 1;
106 }
107 }
108
109 let mut query_builder = sqlx::query(&query);
110 for param in params {
111 query_builder = query_builder.bind(param);
112 }
113
114 let rows = query_builder
115 .fetch_all(&self.pool)
116 .await?
117 .into_iter()
118 .map(|row| DbArticleImage::from_row(&row))
119 .collect::<Result<Vec<_>, _>>()?;
120
121 Ok(rows)
122 }
123
124 async fn create(&self, item: &Self::Item) -> Result<Uuid, Self::Error> {
125 let row = sqlx::query_scalar::<_, Uuid>(
126 r#"
127 INSERT INTO article_images (article_id, image_path, visible)
128 VALUES ($1, $2, $3)
129 RETURNING id
130 "#,
131 )
132 .bind(item.article_id)
133 .bind(&item.image_path)
134 .bind(item.visible)
135 .fetch_one(&self.pool)
136 .await?;
137
138 Ok(row)
139 }
140
141 async fn delete(&self, id: Uuid) -> Result<(), Self::Error> {
142 let result = sqlx::query(
143 r#"
144 DELETE FROM article_images
145 WHERE id = $1
146 "#,
147 )
148 .bind(id)
149 .execute(&self.pool)
150 .await?;
151
152 if result.rows_affected() == 0 {
153 return Err(SqlxError::RowNotFound);
154 }
155
156 Ok(())
157 }
158
159 async fn delete_all(
160 &self,
161 filters: Option<HashMap<String, String>>,
162 ) -> Result<(), Self::Error> {
163 let items = self.list(filters).await?;
164 for item in items {
165 if let Some(id) = item.id {
166 self.delete(id).await?;
167 }
168 }
169 Ok(())
170 }
171
172 async fn update(&self, _item: &Self::Item) -> Result<(), Self::Error> {
173 Err(SqlxError::RowNotFound)
174 }
175}