use std::sync::Arc;
use axum::http::Method;
use futures_util::future::BoxFuture;
use sea_orm::DbErr;
use serde_json::Value;
pub use crate::admin::{
helper::dyn_form::DynForm,
resource::{AdminResource, ColumnFilter, CrudOperation, DisplayConfig},
};
use crate::utils::aliases::{ADb, ATera, StrMap};
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize)]
pub enum SortDir {
#[default]
Asc,
Desc,
}
impl SortDir {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
SortDir::Asc => "asc",
SortDir::Desc => "desc",
}
}
#[must_use]
pub fn toggle(&self) -> &'static str {
match self {
SortDir::Asc => "desc",
SortDir::Desc => "asc",
}
}
}
#[derive(Debug, Clone)]
pub struct ListParams {
pub offset: u64,
pub limit: u64,
pub sort_by: Option<String>,
pub sort_dir: SortDir,
pub search: Option<String>,
pub column_filters: Vec<(String, String)>,
pub scope: Option<(String, String)>,
}
pub type FormBuilder = Arc<
dyn Fn(ADb, Vec<String>, StrMap, ATera, String, Method) -> BoxFuture<'static, Box<dyn DynForm>>
+ Send
+ Sync,
>;
pub type ListFn =
Arc<dyn Fn(ADb, ListParams) -> BoxFuture<'static, Result<Vec<Value>, DbErr>> + Send + Sync>;
pub type GetFn =
Arc<dyn Fn(ADb, String) -> BoxFuture<'static, Result<Option<Value>, DbErr>> + Send + Sync>;
pub type EnumLabelFn = fn(&mut Value);
pub type DeleteFn = Arc<dyn Fn(ADb, String) -> BoxFuture<'static, Result<(), DbErr>> + Send + Sync>;
pub type UpdateFn =
Arc<dyn Fn(ADb, String, StrMap) -> BoxFuture<'static, Result<(), DbErr>> + Send + Sync>;
pub type CreateFn = Arc<dyn Fn(ADb, StrMap) -> BoxFuture<'static, Result<(), DbErr>> + Send + Sync>;
pub type CountFn = Arc<
dyn Fn(ADb, Option<String>, Option<(String, String)>) -> BoxFuture<'static, Result<u64, DbErr>>
+ Send
+ Sync,
>;
pub type FilterFn = Arc<
dyn Fn(
ADb,
std::collections::HashMap<String, u64>,
) -> BoxFuture<
'static,
Result<std::collections::HashMap<String, (Vec<String>, u64)>, DbErr>,
> + Send
+ Sync,
>;
#[derive(Debug, Clone, serde::Serialize)]
pub struct M2mFieldOptions {
pub field_name: String,
pub label: String,
pub choices: Vec<(String, String)>,
pub selected: Vec<String>,
}
pub type M2mLoaderFn =
Arc<dyn Fn(ADb, Option<String>) -> BoxFuture<'static, Vec<M2mFieldOptions>> + Send + Sync>;
#[derive(Debug, Clone, serde::Serialize)]
pub struct GroupAction {
pub field: String,
pub label: String,
pub choices: Vec<(String, String)>,
}
impl GroupAction {
pub fn bool(field: &str, label: &str) -> Self {
Self {
field: field.to_string(),
label: label.to_string(),
choices: vec![
("true".to_string(), "Oui".to_string()),
("false".to_string(), "Non".to_string()),
],
}
}
pub fn val(field: &str, label: &str, value: &str) -> Self {
Self {
field: field.to_string(),
label: label.to_string(),
choices: vec![(value.to_string(), label.to_string())],
}
}
}
pub struct ResourceEntry {
pub meta: AdminResource,
pub form_builder: FormBuilder,
pub edit_form_builder: Option<FormBuilder>,
pub list_fn: Option<ListFn>,
pub get_fn: Option<GetFn>,
pub delete_fn: Option<DeleteFn>,
pub update_fn: Option<UpdateFn>,
pub partial_update_fn: Option<UpdateFn>,
pub create_fn: Option<CreateFn>,
pub count_fn: Option<CountFn>,
pub filter_fn: Option<FilterFn>,
pub group_actions: Vec<GroupAction>,
pub m2m_loader: Option<M2mLoaderFn>,
pub unique_fields: &'static [&'static str],
pub own_field: Option<&'static str>,
pub enum_label_fn: Option<EnumLabelFn>,
}
impl ResourceEntry {
pub fn new(meta: AdminResource, form_builder: FormBuilder) -> Self {
Self {
meta,
form_builder,
edit_form_builder: None,
list_fn: None,
get_fn: None,
delete_fn: None,
update_fn: None,
partial_update_fn: None,
create_fn: None,
count_fn: None,
filter_fn: None,
group_actions: Vec::new(),
m2m_loader: None,
unique_fields: &[],
own_field: None,
enum_label_fn: None,
}
}
#[must_use]
pub fn with_enum_label_fn(mut self, f: EnumLabelFn) -> Self {
self.enum_label_fn = Some(f);
self
}
#[must_use]
pub fn with_m2m_loader(mut self, f: M2mLoaderFn) -> Self {
self.m2m_loader = Some(f);
self
}
#[must_use]
pub fn with_edit_form_builder(mut self, f: FormBuilder) -> Self {
self.edit_form_builder = Some(f);
self
}
#[must_use]
pub fn with_list_fn(mut self, f: ListFn) -> Self {
self.list_fn = Some(f);
self
}
#[must_use]
pub fn with_get_fn(mut self, f: GetFn) -> Self {
self.get_fn = Some(f);
self
}
#[must_use]
pub fn with_delete_fn(mut self, f: DeleteFn) -> Self {
self.delete_fn = Some(f);
self
}
#[must_use]
pub fn with_update_fn(mut self, f: UpdateFn) -> Self {
self.update_fn = Some(f);
self
}
#[must_use]
pub fn with_partial_update_fn(mut self, f: UpdateFn) -> Self {
self.partial_update_fn = Some(f);
self
}
#[must_use]
pub fn with_create_fn(mut self, f: CreateFn) -> Self {
self.create_fn = Some(f);
self
}
#[must_use]
pub fn with_count_fn(mut self, f: CountFn) -> Self {
self.count_fn = Some(f);
self
}
#[must_use]
pub fn with_filter_fn(mut self, f: FilterFn) -> Self {
self.filter_fn = Some(f);
self
}
#[must_use]
pub fn with_unique_fields(mut self, fields: &'static [&'static str]) -> Self {
self.unique_fields = fields;
self
}
#[must_use]
pub fn with_own_field(mut self, field: &'static str) -> Self {
self.own_field = Some(field);
self
}
#[must_use]
pub fn with_group_actions(mut self, actions: Vec<GroupAction>) -> Self {
let mut merged: Vec<GroupAction> = Vec::new();
for action in actions {
if let Some(existing) = merged.iter_mut().find(|a| a.field == action.field) {
existing.choices.extend(action.choices);
} else {
merged.push(action);
}
}
self.group_actions = merged;
self
}
}