use async_trait::async_trait;
use regex::Regex;
use sea_orm::DatabaseConnection;
use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::ActixAdminError;
use crate::{model::ActixAdminModelFilterType, ActixAdminModel, SortOrder};
use actix_session::Session;
use std::convert::From;
pub struct ActixAdminViewModelParams {
pub page: Option<u64>,
pub entities_per_page: Option<u64>,
pub viewmodel_filter: Vec<ActixAdminViewModelFilter>,
pub search: String,
pub sort_by: String,
pub sort_order: SortOrder,
pub tenant_ref: Option<i32>,
}
pub trait ActixAdminPrimaryKey:
serde::de::DeserializeOwned + std::str::FromStr + std::fmt::Display + Clone + 'static
{
}
impl<T> ActixAdminPrimaryKey for T where
T: serde::de::DeserializeOwned + std::str::FromStr + std::fmt::Display + Clone + 'static
{
}
#[async_trait(?Send)]
pub trait ActixAdminViewModelTrait {
type Id: ActixAdminPrimaryKey;
async fn list(
db: &DatabaseConnection,
params: &ActixAdminViewModelParams,
) -> Result<(Option<u64>, Vec<ActixAdminModel>), ActixAdminError>;
async fn create_entity(
db: &DatabaseConnection,
model: ActixAdminModel,
tenant_ref: Option<i32>,
) -> Result<ActixAdminModel, ActixAdminError>;
async fn delete_entity(
db: &DatabaseConnection,
id: Self::Id,
tenant_ref: Option<i32>,
) -> Result<bool, ActixAdminError>;
async fn delete_entities(
db: &DatabaseConnection,
ids: &[Self::Id],
tenant_ref: Option<i32>,
) -> Result<u64, ActixAdminError> {
let mut deleted = 0u64;
for id in ids {
if Self::delete_entity(db, id.clone(), tenant_ref).await? {
deleted += 1;
}
}
Ok(deleted)
}
async fn get_entity(
db: &DatabaseConnection,
id: Self::Id,
tenant_ref: Option<i32>,
) -> Result<ActixAdminModel, ActixAdminError>;
async fn edit_entity(
db: &DatabaseConnection,
id: Self::Id,
model: ActixAdminModel,
tenant_ref: Option<i32>,
) -> Result<ActixAdminModel, ActixAdminError>;
async fn get_select_lists(
db: &DatabaseConnection,
tenant_ref: Option<i32>,
) -> Result<HashMap<String, Vec<(String, String)>>, ActixAdminError>;
async fn get_viewmodel_filter(
db: &DatabaseConnection,
) -> HashMap<String, ActixAdminViewModelFilter>;
async fn validate_entity(model: &mut ActixAdminModel, db: &DatabaseConnection);
fn get_entity_name() -> String;
}
#[derive(Clone, Debug, Serialize)]
pub struct ActixAdminBulkAction {
pub name: String,
pub label: String,
pub icon: Option<String>,
pub confirm: Option<String>,
}
#[derive(Clone)]
pub struct ActixAdminViewModel {
pub entity_name: String,
pub primary_key: String,
pub fields: &'static [ActixAdminViewModelField],
pub show_search: bool,
pub user_can_access: Option<fn(&Session) -> bool>,
pub user_can_create: Option<fn(&Session) -> bool>,
pub user_can_edit: Option<fn(&Session) -> bool>,
pub user_can_delete: Option<fn(&Session) -> bool>,
pub user_can_view_details: Option<fn(&Session) -> bool>,
pub user_can_export: Option<fn(&Session) -> bool>,
pub default_show_aside: bool,
pub inline_edit: bool,
pub bulk_actions: Vec<ActixAdminBulkAction>,
}
#[derive(Clone, Debug, Serialize)]
pub struct ActixAdminViewModelSerializable {
pub entity_name: String,
pub primary_key: String,
pub fields: &'static [ActixAdminViewModelField],
pub show_search: bool,
pub default_show_aside: bool,
pub inline_edit: bool,
#[serde(default)]
pub can_create: bool,
#[serde(default)]
pub can_edit: bool,
#[serde(default)]
pub can_delete: bool,
#[serde(default)]
pub can_view_details: bool,
#[serde(default)]
pub can_export: bool,
pub bulk_actions: Vec<ActixAdminBulkAction>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ActixAdminFilterOperator {
Equals,
NotEquals,
Contains,
NotContains,
GreaterThan,
LessThan,
GreaterEquals,
LessEquals,
IsNull,
IsNotNull,
InList,
}
impl ActixAdminFilterOperator {
pub fn as_str(&self) -> &'static str {
match self {
Self::Equals => "equals",
Self::NotEquals => "not_equals",
Self::Contains => "contains",
Self::NotContains => "not_contains",
Self::GreaterThan => "gt",
Self::LessThan => "lt",
Self::GreaterEquals => "gte",
Self::LessEquals => "lte",
Self::IsNull => "is_null",
Self::IsNotNull => "is_not_null",
Self::InList => "in",
}
}
pub fn label(&self) -> &'static str {
match self {
Self::Equals => "=",
Self::NotEquals => "≠",
Self::Contains => "contains",
Self::NotContains => "does not contain",
Self::GreaterThan => ">",
Self::LessThan => "<",
Self::GreaterEquals => "≥",
Self::LessEquals => "≤",
Self::IsNull => "is empty",
Self::IsNotNull => "is not empty",
Self::InList => "in list",
}
}
pub fn from_str(s: &str) -> Option<Self> {
<Self as std::str::FromStr>::from_str(s).ok()
}
}
impl std::str::FromStr for ActixAdminFilterOperator {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"equals" | "eq" | "=" => Ok(Self::Equals),
"not_equals" | "ne" | "!=" => Ok(Self::NotEquals),
"contains" | "like" => Ok(Self::Contains),
"not_contains" | "not_like" => Ok(Self::NotContains),
"gt" | ">" => Ok(Self::GreaterThan),
"lt" | "<" => Ok(Self::LessThan),
"gte" | ">=" => Ok(Self::GreaterEquals),
"lte" | "<=" => Ok(Self::LessEquals),
"is_null" | "empty" => Ok(Self::IsNull),
"is_not_null" | "not_empty" => Ok(Self::IsNotNull),
"in" | "in_list" => Ok(Self::InList),
_ => Err(()),
}
}
}
#[derive(Clone, Debug, Serialize)]
pub struct ActixAdminViewModelFilter {
pub name: String,
pub value: Option<String>,
pub foreign_key: Option<String>,
pub values: Option<Vec<(String, String)>>,
pub filter_type: Option<ActixAdminModelFilterType>,
#[serde(default)]
pub operators: Vec<ActixAdminFilterOperator>,
#[serde(default)]
pub operator: Option<ActixAdminFilterOperator>,
}
impl ActixAdminViewModelSerializable {
pub fn from_view_model(entity: &ActixAdminViewModel) -> Self {
ActixAdminViewModelSerializable {
entity_name: entity.entity_name.clone(),
primary_key: entity.primary_key.clone(),
fields: entity.fields,
show_search: entity.show_search,
default_show_aside: entity.default_show_aside,
inline_edit: entity.inline_edit,
can_create: false,
can_edit: false,
can_delete: false,
can_view_details: false,
can_export: false,
bulk_actions: entity.bulk_actions.clone(),
}
}
}
impl From<ActixAdminViewModel> for ActixAdminViewModelSerializable {
fn from(entity: ActixAdminViewModel) -> Self {
Self::from_view_model(&entity)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum ActixAdminViewModelFieldType {
Number,
Text,
TextArea,
Checkbox,
Date,
Time,
DateTime,
SelectList,
FileUpload,
Html,
Url,
Email,
Image,
RichText,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ActixAdminViewModelField {
pub field_name: String,
pub html_input_type: String,
pub select_list: String,
pub dateformat: Option<String>,
pub is_option: bool,
pub field_type: ActixAdminViewModelFieldType,
pub list_sort_position: usize,
pub list_hide_column: bool,
#[serde(skip_serializing, skip_deserializing)]
pub list_regex_mask: Option<Regex>,
pub foreign_key: String,
pub is_tenant_ref: bool,
pub ceil: Option<u8>,
pub floor: Option<u8>,
pub shorten: Option<u16>,
pub use_tom_select_callback: bool,
#[serde(default)]
pub readonly: bool,
}
impl ActixAdminViewModelFieldType {
#[allow(clippy::too_many_arguments)]
pub fn get_field_type(
type_path: &str,
select_list: String,
is_textarea: bool,
is_file_upload: bool,
is_image: bool,
is_html: bool,
is_url: bool,
is_email: bool,
is_wysiwyg: bool,
) -> ActixAdminViewModelFieldType {
if !select_list.is_empty() {
return ActixAdminViewModelFieldType::SelectList;
}
if is_image {
return ActixAdminViewModelFieldType::Image;
}
if is_wysiwyg {
return ActixAdminViewModelFieldType::RichText;
}
if is_textarea {
return ActixAdminViewModelFieldType::TextArea;
}
if is_file_upload {
return ActixAdminViewModelFieldType::FileUpload;
}
if is_html {
return ActixAdminViewModelFieldType::Html;
}
if is_url {
return ActixAdminViewModelFieldType::Url;
}
if is_email {
return ActixAdminViewModelFieldType::Email;
}
match type_path {
"i32" => ActixAdminViewModelFieldType::Number,
"i64" => ActixAdminViewModelFieldType::Number,
"usize" => ActixAdminViewModelFieldType::Number,
"String" => ActixAdminViewModelFieldType::Text,
"bool" => ActixAdminViewModelFieldType::Checkbox,
"DateTimeWithTimeZone" => ActixAdminViewModelFieldType::DateTime,
"DateTime" => ActixAdminViewModelFieldType::DateTime,
"Date" => ActixAdminViewModelFieldType::Date,
_ => ActixAdminViewModelFieldType::Text,
}
}
}