diff --git a/actix_admin/actix_admin_macros/src/lib.rs b/actix_admin/actix_admin_macros/src/lib.rs index 1db0e69..a144cef 100644 --- a/actix_admin/actix_admin_macros/src/lib.rs +++ b/actix_admin/actix_admin_macros/src/lib.rs @@ -2,29 +2,20 @@ use proc_macro; use quote::quote; mod struct_fields; -use struct_fields::{ - get_fields_for_tokenstream, - get_fields_for_edit_model, - get_fields_for_from_model, - get_actix_admin_fields_html_input, - get_fields_for_create_model, - get_actix_admin_fields, - get_field_for_primary_key, +use struct_fields::{ + get_actix_admin_fields, get_actix_admin_fields_html_input, + get_actix_admin_fields_is_option_list, get_actix_admin_fields_searchable, + get_actix_admin_fields_select_list, get_actix_admin_fields_type_path_string, + get_field_for_primary_key, get_fields_for_create_model, get_fields_for_edit_model, + get_fields_for_from_model, get_fields_for_tokenstream, get_fields_for_validate_model, get_primary_key_field_name, - get_actix_admin_fields_select_list, - get_actix_admin_fields_is_option_list, - get_fields_for_validate_model, - get_actix_admin_fields_searchable }; mod selectlist_fields; -use selectlist_fields::{ - get_select_list, - get_select_lists -}; +use selectlist_fields::{get_select_list, get_select_lists}; -mod model_fields; mod attributes; +mod model_fields; #[proc_macro_derive(DeriveActixAdminSelectList, attributes(actix_admin))] pub fn derive_actix_admin_select_list(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -46,6 +37,7 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea let field_for_primary_key = get_field_for_primary_key(&fields); let fields_for_validate_model = get_fields_for_validate_model(&fields); let fields_searchable = get_actix_admin_fields_searchable(&fields); + let fields_type_path = get_actix_admin_fields_type_path_string(&fields); let select_lists = get_select_lists(&fields); @@ -120,7 +112,6 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea // TODO: separate primary key from other keys let entity = Entity::find_by_id(id).one(db).await.unwrap().unwrap(); let model = ActixAdminModel::from(entity); - model } @@ -134,10 +125,8 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea let mut entity: ActiveModel = entity.unwrap().into(); #(#fields_for_edit_model);*; - let entity: Model = entity.update(db).await.unwrap(); } - model } @@ -187,12 +176,9 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea (num_pages, model_entities) } - fn validate_model(model: &ActixAdminModel) -> HashMap { let mut errors = HashMap::::new(); - #(#fields_for_validate_model);*; - //let mut custom_errors = Entity.validate(); //errors.append(&mut custom_errors); errors @@ -205,7 +191,7 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea #(#field_names),* ).split(",") .collect::>(); - + let html_input_types = stringify!( #(#field_html_input_type),* ).split(",") @@ -220,14 +206,19 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea #(#is_option_list),* ]; - for (field_name, html_input_type, select_list, is_option_list) in izip!(&field_names, &html_input_types, &field_select_lists, is_option_lists) { - vec.push(ActixAdminViewModelField { - field_name: field_name.replace('"', "").replace(' ', "").to_string(), - html_input_type: html_input_type.replace('"', "").replace(' ', "").to_string(), - select_list: select_list.replace('"', "").replace(' ', "").to_string(), - is_option: is_option_list - }); - } + let fields_type_paths = [ + #(#fields_type_path),* + ]; + + for (field_name, html_input_type, select_list, is_option_list, fields_type_path) in izip!(&field_names, &html_input_types, &field_select_lists, is_option_lists, fields_type_paths) { + vec.push(ActixAdminViewModelField { + field_name: field_name.replace('"', "").replace(' ', "").to_string(), + html_input_type: html_input_type.replace('"', "").replace(' ', "").to_string(), + select_list: select_list.replace('"', "").replace(' ', "").to_string(), + is_option: is_option_list, + field_type: ActixAdminViewModelFieldType::from(fields_type_path) + }); + } vec } } diff --git a/actix_admin/actix_admin_macros/src/model_fields.rs b/actix_admin/actix_admin_macros/src/model_fields.rs index d05db0b..82b3c4c 100644 --- a/actix_admin/actix_admin_macros/src/model_fields.rs +++ b/actix_admin/actix_admin_macros/src/model_fields.rs @@ -28,4 +28,21 @@ impl ModelField { _ => false } } + + pub fn get_type_path_string(&self) -> String { + let type_path_string: String; + if self.is_option() { + match &self.inner_type.clone().unwrap() { + Type::Path(type_path) => type_path_string = type_path.clone().into_token_stream().to_string(), + _ => panic!("not a type path") + } + } else { + match &self.ty { + Type::Path(type_path) => type_path_string = type_path.clone().into_token_stream().to_string(), + _ => panic!("not a type path") + } + } + + type_path_string + } } \ No newline at end of file diff --git a/actix_admin/actix_admin_macros/src/struct_fields.rs b/actix_admin/actix_admin_macros/src/struct_fields.rs index e8e7a80..2ef6be9 100644 --- a/actix_admin/actix_admin_macros/src/struct_fields.rs +++ b/actix_admin/actix_admin_macros/src/struct_fields.rs @@ -43,9 +43,9 @@ pub fn filter_fields(fields: &Fields) -> Vec { (LitStr::from(attr_field)).value() }) }); - let html_input_type = actix_admin_attr.map_or("text".to_string(), |attr| { + let html_input_type = actix_admin_attr.map_or("".to_string(), |attr| { attr.html_input_type - .map_or("text".to_string(), |attr_field| { + .map_or("".to_string(), |attr_field| { (LitStr::from(attr_field)).value() }) }); @@ -140,6 +140,20 @@ pub fn get_actix_admin_fields_is_option_list(fields: &Vec) -> Vec>() } +pub fn get_actix_admin_fields_type_path_string(fields: &Vec) -> Vec { + fields + .iter() + .filter(|model_field| !model_field.primary_key) + .map(|model_field| { + let type_path_string = model_field.get_type_path_string(); + + quote! { + #type_path_string + } + }) + .collect::>() +} + pub fn get_actix_admin_fields_html_input(fields: &Vec) -> Vec { fields .iter() diff --git a/actix_admin/src/lib.rs b/actix_admin/src/lib.rs index 19c3f59..52c9fe2 100644 --- a/actix_admin/src/lib.rs +++ b/actix_admin/src/lib.rs @@ -1,7 +1,8 @@ use lazy_static::lazy_static; use sea_orm::DatabaseConnection; use std::collections::HashMap; -use tera::{Tera}; +use tera::{Tera, Result, Value, to_value, try_get_value }; +use std::{ hash::BuildHasher}; pub mod view_model; pub mod model; @@ -11,7 +12,7 @@ pub mod builder; pub mod prelude { pub use crate::builder::{ ActixAdminBuilder, ActixAdminBuilderTrait}; pub use crate::model::{ ActixAdminModel, ActixAdminModelTrait}; - pub use crate::view_model::{ ActixAdminViewModel, ActixAdminViewModelTrait, ActixAdminViewModelField}; + pub use crate::view_model::{ ActixAdminViewModel, ActixAdminViewModelTrait, ActixAdminViewModelField, ActixAdminViewModelFieldType }; pub use actix_admin_macros::{ DeriveActixAdminModel, DeriveActixAdminSelectList }; pub use crate::{ ActixAdminAppDataTrait, ActixAdmin }; pub use crate::{ hashmap, ActixAdminSelectListTrait }; @@ -30,10 +31,43 @@ macro_rules! hashmap { // globals lazy_static! { - static ref TERA: Tera = - Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")).unwrap(); + static ref TERA: Tera = { + let mut tera = Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")).unwrap(); + tera.register_filter("get_html_input_type", get_html_input_type); + tera.register_filter("get_html_input_class", get_html_input_class); + tera + }; } +pub fn get_html_input_class(value: &tera::Value, _: &HashMap) -> Result { + let field = try_get_value!("get_html_input_class", "value", ActixAdminViewModelField, value); + let html_input_type = match field.field_type { + ActixAdminViewModelFieldType::Checkbox => "checkbox", + _ => "input" + }; + + Ok(to_value(html_input_type).unwrap()) +} + +pub fn get_html_input_type(value: &tera::Value, _: &HashMap) -> Result { + let field = try_get_value!("get_html_input_type", "value", ActixAdminViewModelField, value); + + // TODO: convert to option + if field.html_input_type != "" { + return Ok(to_value(field.html_input_type).unwrap()) + } + + let html_input_type = match field.field_type { + ActixAdminViewModelFieldType::Text => "text", + ActixAdminViewModelFieldType::DateTime => "datetime-local", + ActixAdminViewModelFieldType::Checkbox => "checkbox", + _ => "text" + }; + + Ok(to_value(html_input_type).unwrap()) +} + + // AppDataTrait pub trait ActixAdminAppDataTrait { fn get_db(&self) -> &DatabaseConnection; diff --git a/actix_admin/src/view_model.rs b/actix_admin/src/view_model.rs index 8694057..10153d0 100644 --- a/actix_admin/src/view_model.rs +++ b/actix_admin/src/view_model.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; use sea_orm::DatabaseConnection; -use serde::{Serialize}; +use serde::{Serialize, Deserialize}; use std::collections::HashMap; use crate::ActixAdminModel; +use std::convert::From; #[async_trait(?Send)] pub trait ActixAdminViewModelTrait { @@ -34,10 +35,37 @@ pub struct ActixAdminViewModel { pub fields: Vec, } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum ActixAdminViewModelFieldType { + Number, + Text, + TextArea, + Checkbox, + Date, + Time, + DateTime, + SelectList +} + +impl From<&str> for ActixAdminViewModelFieldType { + fn from(input: &str) -> ActixAdminViewModelFieldType { + match input { + "i32" => ActixAdminViewModelFieldType::Number, + "i64" => ActixAdminViewModelFieldType::Number, + "usize" => ActixAdminViewModelFieldType::Number, + "String" => ActixAdminViewModelFieldType::Text, + "bool" => ActixAdminViewModelFieldType::Checkbox, + "DateTimeWithTimeZone" => ActixAdminViewModelFieldType::DateTime, + _ => ActixAdminViewModelFieldType::Text + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct ActixAdminViewModelField { pub field_name: String, pub html_input_type: String, pub select_list: String, - pub is_option: bool + pub is_option: bool, + pub field_type: ActixAdminViewModelFieldType } \ No newline at end of file diff --git a/actix_admin/templates/base.html b/actix_admin/templates/base.html index 28231ae..0ae02f6 100644 --- a/actix_admin/templates/base.html +++ b/actix_admin/templates/base.html @@ -13,27 +13,50 @@ Actix Admin - + - {% include "header.html" %} -
- {% block content %} - {% endblock content %} -
+ {% include "header.html" %} +
+ {% block content %} + {% endblock content %} +
diff --git a/actix_admin/templates/create_or_edit.html b/actix_admin/templates/create_or_edit.html index ff77133..629c75d 100644 --- a/actix_admin/templates/create_or_edit.html +++ b/actix_admin/templates/create_or_edit.html @@ -4,7 +4,7 @@
{% for model_field in view_model.fields -%}
-
{% else %}
- - - - - \ No newline at end of file diff --git a/database.db-wal b/database.db-wal index bd7b119..2163533 100644 Binary files a/database.db-wal and b/database.db-wal differ diff --git a/src/entity/comment.rs b/src/entity/comment.rs index fa56f23..d3a7d56 100644 --- a/src/entity/comment.rs +++ b/src/entity/comment.rs @@ -11,10 +11,11 @@ pub struct Model { pub id: i32, pub comment: String, #[sea_orm(column_type = "Text")] + #[actix_admin(html_input_type = "email")] pub user: String, #[sea_orm(column_type = "DateTime")] - #[actix_admin(html_input_type = "datetime-local")] - pub insert_date: DateTimeWithTimeZone + pub insert_date: DateTimeWithTimeZone, + pub is_visible: bool } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/src/entity/mod.rs b/src/entity/mod.rs index 673dfc0..e3a1b7a 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -44,6 +44,7 @@ pub async fn create_post_table(db: &DbConn) -> Result { .col(ColumnDef::new(comment::Column::Comment).string().not_null()) .col(ColumnDef::new(comment::Column::User).string().not_null()) .col(ColumnDef::new(comment::Column::InsertDate).date_time().not_null()) + .col(ColumnDef::new(comment::Column::IsVisible).boolean().not_null()) .to_owned(); create_table(db, &stmt).await diff --git a/src/entity/post.rs b/src/entity/post.rs index d7c555f..748dcb4 100644 --- a/src/entity/post.rs +++ b/src/entity/post.rs @@ -1,9 +1,9 @@ use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; use actix_admin::prelude::*; -use std::str::FromStr; use std::fmt; use std::fmt::Display; +use std::str::FromStr; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize, DeriveActixAdminModel)] #[sea_orm(table_name = "post")]