1use std::cmp;
2#[cfg(feature = "seaorm")]
3use std::collections::BTreeMap;
4use std::hash::Hash;
5
6use actix_cloud::utils;
7use anyhow::Result;
8use derivative::Derivative;
9#[cfg(feature = "seaorm")]
10use sea_orm::{
11 ExprTrait as _, FromQueryResult, Order, QueryOrder as _,
12 entity::prelude::async_trait::async_trait,
13 prelude::*,
14 sea_query::{ColumnRef, LikeExpr, SimpleExpr},
15};
16use serde::{Deserialize, Serialize};
17use serde_inline_default::serde_inline_default;
18use serde_with::{DisplayFromStr, serde_as};
19use validator::{Validate, ValidationError};
20
21use crate::HyUuid;
22#[cfg(feature = "entity")]
23use crate::entity::DefaultColumnTrait;
24#[cfg(feature = "seaorm")]
25use crate::utils::StringUtil as _;
26
27#[derive(Serialize, Debug)]
28pub struct PageData<T> {
29 pub total: u64,
30 pub data: Vec<T>,
31}
32
33impl<T> PageData<T> {
34 pub fn new(data: (Vec<T>, u64)) -> Self {
35 Self {
36 total: data.1,
37 data: data.0,
38 }
39 }
40}
41
42#[serde_as]
43#[serde_inline_default]
44#[derive(Derivative, Deserialize, Validate, Clone, Debug)]
45#[derivative(Default(new = "true"))]
46pub struct PaginationParam {
47 #[validate(range(min = 1))]
48 #[serde_inline_default(1)]
49 #[derivative(Default(value = "1"))]
50 #[serde_as(as = "DisplayFromStr")]
51 pub page: u64,
52
53 #[validate(range(min = 1, max = 100))]
54 #[serde_inline_default(10)]
55 #[derivative(Default(value = "10"))]
56 #[serde_as(as = "DisplayFromStr")]
57 pub size: u64,
58}
59
60impl PaginationParam {
61 pub fn left(&self) -> u64 {
62 self.page.saturating_sub(1).saturating_mul(self.size)
63 }
64
65 pub fn right(&self) -> u64 {
66 self.page.saturating_mul(self.size)
67 }
68
69 pub fn round(p: u64) -> usize {
70 cmp::min(p, usize::MAX as u64) as usize
71 }
72
73 pub fn split<T>(&self, data: Vec<T>) -> PageData<T> {
74 let cnt = data.len();
75 PageData::new((
76 data.into_iter()
77 .skip(Self::round(self.left()))
78 .take(Self::round(self.size))
79 .collect(),
80 cnt as u64,
81 ))
82 }
83}
84
85#[cfg(feature = "seaorm")]
86#[async_trait]
87pub trait SelectPage<E, C>
88where
89 E: EntityTrait,
90 C: ConnectionTrait,
91{
92 async fn select_page(self, db: &C, p: Option<PaginationParam>) -> Result<(Vec<E::Model>, u64)>;
93}
94
95#[cfg(feature = "seaorm")]
96#[async_trait]
97impl<M, E, C> SelectPage<E, C> for Select<E>
98where
99 E: EntityTrait<Model = M>,
100 M: FromQueryResult + Sized + Send + Sync,
101 C: ConnectionTrait,
102{
103 async fn select_page(self, db: &C, p: Option<PaginationParam>) -> Result<(Vec<E::Model>, u64)> {
104 if let Some(page) = p {
105 let q = self.paginate(db, page.size);
106 Ok((q.fetch_page(page.page - 1).await?, q.num_items().await?))
107 } else {
108 let res = self.all(db).await?;
109 let cnt = res.len() as u64;
110 Ok((res, cnt))
111 }
112 }
113}
114
115#[cfg(feature = "seaorm")]
116pub trait IntoExpr {
117 fn like_expr<T>(&self, col: T) -> SimpleExpr
118 where
119 T: ColumnTrait;
120}
121
122#[cfg(feature = "seaorm")]
123impl IntoExpr for &String {
124 fn like_expr<T>(&self, col: T) -> SimpleExpr
125 where
126 T: ColumnTrait,
127 {
128 sea_orm::prelude::Expr::col((col.entity_name(), col))
129 .like(LikeExpr::new(format!("%{}%", self.like_escape())).escape('\\'))
130 }
131}
132
133#[cfg(feature = "seaorm")]
134pub struct Condition {
135 pub page: Option<PaginationParam>,
136 pub cond: sea_orm::Condition,
137 pub order: Vec<(SimpleExpr, Order)>,
138}
139
140#[cfg(feature = "seaorm")]
141impl Default for Condition {
142 fn default() -> Self {
143 Self {
144 cond: sea_orm::Condition::all(),
145 page: None,
146 order: Vec::new(),
147 }
148 }
149}
150
151#[cfg(feature = "seaorm")]
152impl From<sea_orm::Condition> for Condition {
153 fn from(cond: sea_orm::Condition) -> Self {
154 Self::new(cond)
155 }
156}
157
158#[cfg(feature = "seaorm")]
159impl Condition {
160 pub fn new(cond: sea_orm::Condition) -> Self {
161 Self {
162 cond,
163 page: None,
164 order: Vec::new(),
165 }
166 }
167
168 pub fn new_all() -> Self {
169 Self::new(Self::all())
170 }
171
172 pub fn new_any() -> Self {
173 Self::new(Self::any())
174 }
175
176 pub fn add_sort(mut self, col: SimpleExpr, sort: Order) -> Self {
177 self.order.push((col, sort));
178 self
179 }
180
181 pub fn add_page(mut self, page: PaginationParam) -> Self {
182 self.page = Some(page);
183 self
184 }
185
186 pub fn parse_sort(mut self, str: String, col: Vec<ColumnRef>) -> Self {
187 let mut allow: BTreeMap<String, ColumnRef> = col
188 .into_iter()
189 .map(|x| (x.column().unwrap().to_string(), x))
190 .collect();
191 for i in str.split(',') {
192 let order = if i.starts_with("-") {
193 Order::Desc
194 } else {
195 Order::Asc
196 };
197 let name = i
198 .strip_prefix('+')
199 .or_else(|| i.strip_prefix('-'))
200 .unwrap_or(i);
201 if let Some(x) = allow.remove(name) {
202 self = self.add_sort(SimpleExpr::Column(x), order);
203 }
204 }
205 self
206 }
207
208 pub fn any() -> sea_orm::Condition {
209 sea_orm::Condition::any()
210 }
211
212 pub fn all() -> sea_orm::Condition {
213 sea_orm::Condition::all()
214 }
215
216 #[allow(clippy::should_implement_trait)]
217 pub fn add<C>(mut self, condition: C) -> Self
218 where
219 C: Into<sea_orm::Condition>,
220 {
221 self.cond = self.cond.add(condition.into());
222 self
223 }
224
225 pub fn add_option<C>(mut self, condition: Option<C>) -> Self
226 where
227 C: Into<sea_orm::Condition>,
228 {
229 self.cond = self.cond.add_option(condition);
230 self
231 }
232
233 pub fn build<E>(self, mut q: Select<E>) -> (Select<E>, Option<PaginationParam>)
234 where
235 E: EntityTrait,
236 {
237 for i in self.order {
238 q = q.order_by(i.0, i.1);
239 }
240 (q.filter(self.cond), self.page)
241 }
242
243 pub async fn select_page<M, E, C>(self, q: Select<E>, db: &C) -> Result<(Vec<E::Model>, u64)>
244 where
245 E: EntityTrait<Model = M>,
246 M: FromQueryResult + Sized + Send + Sync,
247 C: ConnectionTrait,
248 {
249 let (q, page) = self.build(q);
250 q.select_page(db, page).await
251 }
252
253 #[cfg(feature = "entity")]
254 pub fn add_time<T>(mut self, p: &TimeParam) -> Self
255 where
256 T: DefaultColumnTrait,
257 {
258 self = self.add_option(p.created_start.map(|x| T::get_created_at().gte(x)));
259 self = self.add_option(p.created_end.map(|x| T::get_created_at().lte(x)));
260 self = self.add_option(p.updated_start.map(|x| T::get_updated_at().gte(x)));
261 self = self.add_option(p.updated_end.map(|x| T::get_updated_at().lte(x)));
262 self
263 }
264}
265
266pub fn unique_validator<T: Eq + Hash>(x: &Vec<T>) -> Result<(), ValidationError> {
269 if utils::is_unique(x) {
270 Ok(())
271 } else {
272 Err(ValidationError::new("not unique"))
273 }
274}
275
276#[derive(Debug, Validate, Deserialize)]
277pub struct IDsReq {
278 #[validate(custom(function = "unique_validator"))]
279 pub id: Vec<HyUuid>,
280}
281
282#[derive(Debug, Validate, Deserialize)]
283pub struct OptionIDsReq {
284 #[validate(custom(function = "unique_validator"))]
285 pub id: Option<Vec<HyUuid>>,
286}
287
288#[serde_as]
289#[derive(Debug, Deserialize, Validate)]
290pub struct TimeParam {
291 #[validate(range(min = 0))]
292 #[serde_as(as = "Option<DisplayFromStr>")]
293 pub created_start: Option<i64>,
294 #[validate(range(min = 0))]
295 #[serde_as(as = "Option<DisplayFromStr>")]
296 pub created_end: Option<i64>,
297
298 #[validate(range(min = 0))]
299 #[serde_as(as = "Option<DisplayFromStr>")]
300 pub updated_start: Option<i64>,
301 #[validate(range(min = 0))]
302 #[serde_as(as = "Option<DisplayFromStr>")]
303 pub updated_end: Option<i64>,
304}