chore: normalize code

This commit is contained in:
Maulana Sodiqin
2025-05-17 02:08:59 +07:00
parent 874f25aa80
commit a43ef8ec04
104 changed files with 691 additions and 655 deletions
+8
View File
@@ -0,0 +1,8 @@
pub trait Crud<T, Args> {
fn list(&self, _args: Args) -> T {
unimplemented!("list() is not implemented for this type")
}
fn detail(&self, _args: Args) -> T {
unimplemented!("detail() is not implemented for this type")
}
}
+4
View File
@@ -2,17 +2,20 @@ use imphnen_entities::*;
use imphnen_libs::*;
pub mod bind_filter;
pub mod crud;
pub mod extract_email;
pub mod generate_date;
pub mod generate_otp;
pub mod get_id;
pub mod make_thing;
pub mod mock_test;
pub mod query_builder;
pub mod query_list;
pub mod response_format;
pub mod validator;
pub use bind_filter::*;
pub use crud::*;
pub use extract_email::*;
pub use generate_date::*;
pub use generate_otp::*;
@@ -21,6 +24,7 @@ pub use imphnen_entities::*;
pub use imphnen_libs::*;
pub use make_thing::*;
pub use mock_test::*;
pub use query_builder::*;
pub use query_list::*;
pub use response_format::*;
pub use validator::*;
+137
View File
@@ -0,0 +1,137 @@
use imphnen_libs::MetaRequestDto;
pub struct ListQueryBuilder {
resource: String,
conditions: Vec<String>,
limit: usize,
start: usize,
order_by: Option<String>,
order: Option<String>,
fetch: Vec<String>,
select_fields: Vec<String>,
}
impl ListQueryBuilder {
pub fn from_meta(
resource: impl Into<String>,
meta: &MetaRequestDto,
search_field: impl Into<String>,
select_fields: Option<Vec<&str>>,
) -> Self {
let mut builder = Self::new(resource)
.with_search(meta.search.as_deref(), &search_field.into())
.with_filter(meta.filter_by.as_deref(), meta.filter.as_deref())
.with_sorting(meta.sort_by.as_deref(), meta.order.as_deref())
.with_pagination(meta.page, meta.per_page);
if let Some(fields) = select_fields {
builder = builder.with_select_fields(fields);
}
builder
}
pub fn new(resource: impl Into<String>) -> Self {
Self {
resource: resource.into(),
conditions: vec!["is_deleted = false".into()],
limit: 10,
start: 0,
order_by: None,
order: None,
fetch: vec![],
select_fields: vec![],
}
}
pub fn with_select_fields(mut self, fields: Vec<&str>) -> Self {
self.select_fields = fields.into_iter().map(String::from).collect();
self
}
pub fn with_search(mut self, search: Option<&str>, field: &str) -> Self {
if let Some(search) = search {
if !search.is_empty() {
self
.conditions
.push(format!("string::contains({} ?? '', $search)", field));
}
}
self
}
pub fn with_filter(mut self, field: Option<&str>, value: Option<&str>) -> Self {
if let (Some(f), Some(v)) = (field, value) {
if !v.is_empty() {
self.conditions.push(format!("{} = $filter", f));
}
}
self
}
pub fn with_pagination(
mut self,
page: Option<u64>,
per_page: Option<u64>,
) -> Self {
let limit = per_page.unwrap_or(10).max(1);
let page = page.unwrap_or(1).max(1);
self.limit = limit as usize;
self.start = ((page - 1) * limit) as usize;
self
}
pub fn with_sorting(mut self, sort_by: Option<&str>, order: Option<&str>) -> Self {
self.order_by = sort_by.map(|s| s.to_string());
self.order = order.map(|o| o.to_uppercase());
self
}
pub fn with_fetch(mut self, fetch: impl Into<String>) -> Self {
self.fetch.push(fetch.into());
self
}
pub fn build(self) -> String {
let where_clause = if !self.conditions.is_empty() {
format!("WHERE {}", self.conditions.join(" AND "))
} else {
String::new()
};
let order_clause = if let Some(field) = self.order_by {
let ord = self.order.unwrap_or_else(|| "ASC".into());
format!("ORDER BY {} {}", field, ord)
} else {
String::new()
};
let fetch_clause = if !self.fetch.is_empty() {
format!("FETCH {}", self.fetch.join(", "))
} else {
String::new()
};
let select_clause = if self.select_fields.is_empty() {
"*".to_string()
} else {
self.select_fields.join(", ")
};
format!(
r#"
SELECT {} FROM {}
{}
{}
LIMIT {} START {}
{}
"#,
select_clause,
self.resource,
where_clause,
order_clause,
self.limit,
self.start,
fetch_clause
)
}
}
+31 -45
View File
@@ -1,8 +1,8 @@
use super::bind_filter_value;
use super::{ListQueryBuilder, bind_filter_value};
use crate::{CountResult, MetaRequestDto, MetaResponseDto, ResponseListSuccessDto};
use anyhow::{bail, Result};
use serde::{de::DeserializeOwned, Serialize};
use surrealdb::{engine::remote::ws::Client, Surreal};
use anyhow::Result;
use serde::{Serialize, de::DeserializeOwned};
use surrealdb::{Surreal, engine::remote::ws::Client};
pub async fn query_list_with_meta<T>(
db: &Surreal<Client>,
@@ -10,75 +10,61 @@ pub async fn query_list_with_meta<T>(
meta: &MetaRequestDto,
conditions: Vec<String>,
custom_select: Option<String>,
search_field: &str,
select_fields: Option<Vec<&str>>,
) -> Result<ResponseListSuccessDto<Vec<T>>>
where
T: DeserializeOwned + Serialize,
{
let page = meta.page.unwrap_or(1);
let per_page = meta.per_page.unwrap_or(10);
if page < 1 || per_page < 1 {
bail!("Invalid pagination: page and per_page must be greater than 0");
}
let page = meta.page.unwrap_or(1).max(1);
let per_page = meta.per_page.unwrap_or(10).max(1);
let start = (page - 1) * per_page;
let sql = custom_select.unwrap_or_else(|| {
let mut s = format!("SELECT * FROM {}", table);
if !conditions.is_empty() {
s.push_str(" WHERE ");
s.push_str(&conditions.join(" AND "));
}
if let Some(sort_by) = &meta.sort_by {
let order = match meta
.order
.clone()
.unwrap_or_default()
.to_uppercase()
.as_str()
{
"DESC" => "DESC",
_ => "ASC",
};
s.push_str(&format!(" ORDER BY {} {}", sort_by, order));
}
s.push_str(" LIMIT $per_page START $start");
s
ListQueryBuilder::from_meta(table, meta, search_field, select_fields).build()
});
let mut query_exec = db.query(sql);
if let Some(search) = &meta.search {
if !search.is_empty() {
query_exec = query_exec.bind(("search", search.clone()));
}
}
if let Some(filter_val) = meta.filter.clone() {
query_exec = bind_filter_value(query_exec, filter_val);
if let Some(filter_val) = &meta.filter {
query_exec = bind_filter_value(query_exec, filter_val.clone());
}
query_exec = query_exec
.bind(("per_page", per_page))
.bind(("start", start));
let raw: Vec<T> = query_exec.await?.take(0)?;
let mut count_sql = format!("SELECT count() FROM {}", table);
if !conditions.is_empty() {
count_sql.push_str(" WHERE ");
count_sql.push_str(&conditions.join(" AND "));
}
let mut count_query = db.query(count_sql);
let mut count_query = db.query(format!(
"SELECT count() FROM {} {}",
table,
if conditions.is_empty() {
String::new()
} else {
format!("WHERE {}", conditions.join(" AND "))
}
));
if let Some(search) = &meta.search {
if !search.is_empty() {
count_query = count_query.bind(("search", search.clone()));
}
}
if let Some(filter_val) = meta.filter.clone() {
count_query = bind_filter_value(count_query, filter_val);
if let Some(filter_val) = &meta.filter {
count_query = bind_filter_value(count_query, filter_val.clone());
}
let count_result: Vec<CountResult> = count_query.await?.take(0)?;
let total = count_result.first().map(|c| c.count);
let meta = MetaResponseDto {
page: Some(page),
per_page: Some(per_page),
total,
};
Ok(ResponseListSuccessDto {
data: raw,
meta: Some(meta),
meta: Some(MetaResponseDto {
page: Some(page),
per_page: Some(per_page),
total,
}),
})
}