eversal_lib/database/
inv_category.rs1use sqlx::{Postgres, QueryBuilder};
2
3use crate::{LibResult, Error};
4
5const TABLE_NAME: &str = "inv_categories";
6
7#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)]
8pub struct CategoryDb {
9 #[serde(rename = "categoryID")]
10 pub category_id: i32,
11 #[serde(rename = "categoryName")]
12 pub category_name: String,
13 #[serde(rename = "iconID")]
14 pub icon_id: Option<i32>,
15 pub published: bool,
16}
17
18#[derive(Debug, serde::Serialize, serde::Deserialize)]
19pub struct CategoryFilter {
20 pub category_id: Option<Vec<i32>>,
21 pub category_name: Option<Vec<String>>,
22 pub page: Option<i64>,
23 pub limit: Option<i64>,
24}
25
26impl CategoryDb {
27 pub async fn get_by_id(category_id: i32) -> LibResult<Self> {
31 let pool = crate::pool();
32 let category = sqlx::query_as::<_, Self>(&format!(
33 r#"
34 SELECT * FROM {}
35 WHERE category_id = $1
36 "#,
37 TABLE_NAME
38 ))
39 .bind(category_id)
40 .fetch_one(pool)
41 .await?;
42
43 Ok(category)
44 }
45
46 pub async fn count(filter: &CategoryFilter) -> LibResult<i64> {
50 let pool = crate::pool();
51 let mut query = QueryBuilder::new(format!("SELECT COUNT(*) FROM {}", TABLE_NAME));
52 query = Self::build_query(query, filter);
53 let query = query.build_query_scalar();
54
55 match query.fetch_one(pool).await {
56 Ok(count) => Ok(count),
57 Err(err) => {
58 log::error!("Error counting items: {}", err);
59 return Err(Error::new(500, format!("Error counting items: {}", err)));
60 }
61 }
62 }
63
64 pub async fn get_multiple(params: &CategoryFilter) -> LibResult<Vec<Self>> {
68 let page = params.page.unwrap_or(1);
69 let limit = params.limit.unwrap_or(100);
70
71 let pool = crate::pool();
72 let mut query = QueryBuilder::new(format!("SELECT * FROM {}", TABLE_NAME));
73 query = Self::build_query(query, params);
74 query.push(" ORDER BY category_id LIMIT ");
75 query.push_bind(limit);
76 query.push(" OFFSET ");
77 query.push_bind((page - 1) * limit);
78 let query = query.build_query_as();
79
80 match query.fetch_all(pool).await {
81 Ok(items) => Ok(items),
82 Err(err) => {
83 log::error!("Error fetching categories: {}", err);
84 return Err(Error::new(
85 500,
86 format!("Error fetching categories: {}", err),
87 ));
88 }
89 }
90 }
91
92 pub async fn insert(&self) -> LibResult<()> {
96 let pool = crate::pool();
97 sqlx::query(&format!(
98 "INSERT INTO {} (
99 category_id,
100 category_name,
101 published
102 ) VALUES (
103 $1, $2, $3
104 )",
105 TABLE_NAME
106 ))
107 .bind(self.category_id)
108 .bind(&self.category_name)
109 .bind(&self.published)
110 .execute(pool)
111 .await?;
112 Ok(())
113 }
114
115 pub async fn insert_multiple(categories: Vec<Self>) -> LibResult<()> {
119 let pool = crate::pool();
120 let step = 1000;
121 for i in (0..categories.len()).step_by(step) {
122 let mut query = sqlx::QueryBuilder::new(format!(
123 r#"
124 INSERT INTO {} (category_id, category_name, icon_id, published)
125 VALUES
126 "#,
127 TABLE_NAME
128 ));
129
130 for j in 0..step {
131 if i + j >= categories.len() {
132 break;
133 }
134 if j > 0 {
135 query.push(", ");
136 }
137 let category = &categories[i + j];
138 query
139 .push(" (")
140 .push_bind(category.category_id)
141 .push(", ")
142 .push_bind(&category.category_name)
143 .push(", ")
144 .push_bind(category.icon_id)
145 .push(", ")
146 .push_bind(category.published)
147 .push(") ");
148 }
149 query
150 .push("ON CONFLICT (category_id) DO UPDATE SET ")
151 .push("category_name = EXCLUDED.category_name, ")
152 .push("icon_id = EXCLUDED.icon_id, ")
153 .push("published = EXCLUDED.published");
154 query.build().execute(pool).await?;
155 }
156 Ok(())
157 }
158
159 pub async fn update(&self) -> LibResult<()> {
163 let pool = crate::pool();
164 sqlx::query(&format!(
165 "UPDATE {} SET
166 category_name = $2,
167 published = $3
168 WHERE category_id = $1",
169 TABLE_NAME
170 ))
171 .bind(self.category_id)
172 .bind(&self.category_name)
173 .bind(&self.published)
174 .execute(pool)
175 .await?;
176 Ok(())
177 }
178
179 pub async fn delete(category_id: i32) -> LibResult<()> {
183 let pool = crate::pool();
184 sqlx::query(&format!(
185 "DELETE FROM {} WHERE category_id = $1",
186 TABLE_NAME
187 ))
188 .bind(category_id)
189 .execute(pool)
190 .await?;
191 Ok(())
192 }
193
194 fn build_query<'a>(
198 mut query: QueryBuilder<'a, Postgres>,
199 filter: &'a CategoryFilter,
200 ) -> QueryBuilder<'a, Postgres> {
201 let mut has_where = false;
202
203 if let Some(category_names) = &filter.category_name {
205 if !has_where {
206 query.push(" WHERE");
207 has_where = true;
208 } else {
209 query.push(" AND");
210 }
211 query.push(" (");
212 for (i, category_name) in category_names.iter().enumerate() {
213 if i > 0 {
214 query.push(" OR");
215 }
216 query.push(" category_name ILIKE ");
217 query.push_bind(format!("%{}%", category_name));
218 }
219 query.push(" )");
220 }
221
222 if let Some(ids) = &filter.category_id {
224 if !has_where {
225 query.push(" WHERE");
226 } else {
227 query.push(" AND");
228 }
229 query.push(" (");
230 for (i, category_id) in ids.iter().enumerate() {
231 if i > 0 {
232 query.push(" OR");
233 }
234 query.push(" category_id = ");
235 query.push_bind(category_id);
236 }
237 query.push(" )");
238 }
239
240 query
241 }
242}