add option for html input type on column

This commit is contained in:
manuel 2022-07-17 22:12:18 +02:00
parent 96b069042e
commit 2b653510b2
12 changed files with 62 additions and 32 deletions

View File

@ -6,10 +6,12 @@ pub mod derive_attr {
Eq, Eq,
PartialEq, PartialEq,
FromAttributes, FromAttributes,
Default Default,
Clone
)] )]
pub struct ActixAdmin { pub struct ActixAdmin {
pub primary_key: Option<()> pub primary_key: Option<()>,
pub html_input_type: Option<syn::LitStr>
//pub inner_type: Option<syn::Type>, //pub inner_type: Option<syn::Type>,
// Anything that implements `syn::parse::Parse` is supported. // Anything that implements `syn::parse::Parse` is supported.

View File

@ -2,17 +2,17 @@ use proc_macro;
use quote::quote; use quote::quote;
mod struct_fields; mod struct_fields;
use struct_fields::{ get_fields_for_tokenstream, get_fields_for_edit_model, get_fields_for_from_model, get_fields_for_create_model, get_field_names, get_field_for_primary_key, get_primary_key_field_name}; 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, get_primary_key_field_name};
mod model_fields; mod model_fields;
mod attributes; mod attributes;
#[proc_macro_derive(DeriveActixAdminModel, attributes(actix_admin))] #[proc_macro_derive(DeriveActixAdminModel, attributes(actix_admin))]
pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let fields = get_fields_for_tokenstream(input); let fields = get_fields_for_tokenstream(input);
let names_const_fields_str = get_field_names(&fields); let field_names = get_actix_admin_fields(&fields);
let field_html_input_type = get_actix_admin_fields_html_input(&fields);
let name_primary_field_str = get_primary_key_field_name(&fields); let name_primary_field_str = get_primary_key_field_name(&fields);
let fields_for_create_model = get_fields_for_create_model(&fields); let fields_for_create_model = get_fields_for_create_model(&fields);
let fields_for_edit_model = get_fields_for_edit_model(&fields); let fields_for_edit_model = get_fields_for_edit_model(&fields);
@ -21,6 +21,7 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
let expanded = quote! { let expanded = quote! {
use std::convert::From; use std::convert::From;
use std::iter::zip;
use async_trait::async_trait; use async_trait::async_trait;
use actix_web::{web, HttpResponse, HttpRequest, Error}; use actix_web::{web, HttpResponse, HttpRequest, Error};
use actix_admin::prelude::*; use actix_admin::prelude::*;
@ -135,21 +136,32 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
(num_pages, model_entities) (num_pages, model_entities)
} }
fn get_fields() -> Vec<(String, ActixAdminField)> { fn get_fields() -> Vec<(String, String)> {
let mut vec = Vec::new(); let mut vec = Vec::new();
let field_names = stringify!( let field_names = stringify!(
#(#names_const_fields_str),* #(#field_names),*
).split(",") ).split(",")
.collect::<Vec<_>>() .collect::<Vec<_>>();
.into_iter()
.for_each( |field_name| let html_input_types = stringify!(
#(#field_html_input_type),*
).split(",")
.collect::<Vec<_>>();
let mut names_and_input_type = zip(field_names, html_input_types);
names_and_input_type
.for_each( |field_name_and_type_tuple|
vec.push(( vec.push((
field_name field_name_and_type_tuple.0
.replace('"', "") .replace('"', "")
.replace(' ', "") .replace(' ', "")
.to_string(), .to_string(),
// TODO: match correct ActixAdminField Value // TODO: match correct ActixAdminField Value
ActixAdminField::Text field_name_and_type_tuple.1
.replace('"', "")
.replace(' ', "")
.to_string()
) )
) )
); );

View File

@ -8,7 +8,8 @@ pub struct ModelField {
pub ty: Type, pub ty: Type,
// struct field is option<> // struct field is option<>
pub inner_type: Option<Type>, pub inner_type: Option<Type>,
pub primary_key: bool pub primary_key: bool,
pub html_input_type: String
} }
impl ModelField { impl ModelField {

View File

@ -1,6 +1,6 @@
use proc_macro2::{Span, Ident, TokenStream}; use proc_macro2::{Span, Ident, TokenStream};
use syn::{ use syn::{
Fields, DeriveInput Fields, DeriveInput, LitStr
}; };
use quote::quote; use quote::quote;
use crate::attributes::derive_attr; use crate::attributes::derive_attr;
@ -30,14 +30,18 @@ pub fn filter_fields(fields: &Fields) -> Vec<ModelField> {
let field_ident = field.ident.as_ref().unwrap().clone(); let field_ident = field.ident.as_ref().unwrap().clone();
let inner_type = extract_type_from_option(&field.ty); let inner_type = extract_type_from_option(&field.ty);
let field_ty = field.ty.to_owned(); let field_ty = field.ty.to_owned();
let is_primary_key = actix_admin_attr.map_or(false, |attr| attr.primary_key.is_some()); let is_primary_key = actix_admin_attr.clone().map_or(false, |attr| attr.primary_key.is_some());
let html_input_type = actix_admin_attr.map_or("text".to_string(), |attr| attr.html_input_type.map_or("text".to_string(),
|attr_field| (LitStr::from(attr_field)).value()
));
let model_field = ModelField { let model_field = ModelField {
visibility: field_vis, visibility: field_vis,
ident: field_ident, ident: field_ident,
ty: field_ty, ty: field_ty,
inner_type: inner_type, inner_type: inner_type,
primary_key: is_primary_key primary_key: is_primary_key,
html_input_type: html_input_type
}; };
Some(model_field) Some(model_field)
@ -92,12 +96,13 @@ fn extract_type_from_option(ty: &syn::Type) -> Option<syn::Type> {
}) })
} }
pub fn get_field_names(fields: &Vec<ModelField>) -> Vec<TokenStream> { pub fn get_actix_admin_fields(fields: &Vec<ModelField>) -> Vec<TokenStream> {
fields fields
.iter() .iter()
.filter(|model_field| !model_field.primary_key) .filter(|model_field| !model_field.primary_key)
.map(|model_field| { .map(|model_field| {
let ident_name = model_field.ident.to_string(); let ident_name = model_field.ident.to_string();
quote! { quote! {
#ident_name #ident_name
} }
@ -105,6 +110,20 @@ pub fn get_field_names(fields: &Vec<ModelField>) -> Vec<TokenStream> {
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
pub fn get_actix_admin_fields_html_input(fields: &Vec<ModelField>) -> Vec<TokenStream> {
fields
.iter()
.filter(|model_field| !model_field.primary_key)
.map(|model_field| {
let html_input_type = model_field.html_input_type.to_string();
quote! {
#html_input_type
}
})
.collect::<Vec<_>>()
}
pub fn get_field_for_primary_key(fields: &Vec<ModelField>) -> TokenStream { pub fn get_field_for_primary_key(fields: &Vec<ModelField>) -> TokenStream {
let primary_key_model_field = fields let primary_key_model_field = fields
.iter() .iter()

View File

@ -1,8 +1,8 @@
use lazy_static::lazy_static; use lazy_static::lazy_static;
use sea_orm::DatabaseConnection; use sea_orm::DatabaseConnection;
use serde::{Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use tera::{Tera}; use tera::{Tera};
use serde::{Serialize};
pub mod view_model; pub mod view_model;
pub mod model; pub mod model;
@ -16,7 +16,6 @@ pub mod prelude {
pub use actix_admin_macros::{ DeriveActixAdminModel }; pub use actix_admin_macros::{ DeriveActixAdminModel };
pub use crate::{ ActixAdminAppDataTrait, ActixAdmin}; pub use crate::{ ActixAdminAppDataTrait, ActixAdmin};
pub use crate::{ hashmap }; pub use crate::{ hashmap };
pub use crate::{ ActixAdminField };
} }
use crate::prelude::*; use crate::prelude::*;
@ -36,12 +35,6 @@ lazy_static! {
Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")).unwrap(); Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")).unwrap();
} }
// Fields
#[derive(Clone, Debug, Serialize)]
pub enum ActixAdminField {
Text,
}
// AppDataTrait // AppDataTrait
pub trait ActixAdminAppDataTrait { pub trait ActixAdminAppDataTrait {
fn get_db(&self) -> &DatabaseConnection; fn get_db(&self) -> &DatabaseConnection;

View File

@ -3,8 +3,6 @@ use sea_orm::DatabaseConnection;
use serde::{Serialize}; use serde::{Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use crate::ActixAdminField;
#[async_trait] #[async_trait]
pub trait ActixAdminModelTrait { pub trait ActixAdminModelTrait {
async fn list_model( async fn list_model(
@ -12,7 +10,7 @@ pub trait ActixAdminModelTrait {
page: usize, page: usize,
posts_per_page: usize, posts_per_page: usize,
) -> (usize, Vec<ActixAdminModel>); ) -> (usize, Vec<ActixAdminModel>);
fn get_fields() -> Vec<(String, ActixAdminField)>; fn get_fields() -> Vec<(String, String)>;
} }
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]

View File

@ -3,7 +3,6 @@ use sea_orm::DatabaseConnection;
use serde::{Serialize}; use serde::{Serialize};
use crate::ActixAdminModel; use crate::ActixAdminModel;
use crate::ActixAdminField;
#[async_trait(?Send)] #[async_trait(?Send)]
pub trait ActixAdminViewModelTrait { pub trait ActixAdminViewModelTrait {
@ -26,5 +25,5 @@ pub trait ActixAdminViewModelTrait {
pub struct ActixAdminViewModel { pub struct ActixAdminViewModel {
pub entity_name: String, pub entity_name: String,
pub primary_key: String, pub primary_key: String,
pub fields: Vec<(String, ActixAdminField)>, pub fields: Vec<(String, String)>,
} }

View File

@ -4,9 +4,10 @@
<form method="post"> <form method="post">
<div class="grid"> <div class="grid">
{% for model_field in view_model.fields -%} {% for model_field in view_model.fields -%}
<input type="text" name="{{ model_field[0] }}" placeholder="{{ model_field[0] }}" aria-label="{{ model_field[0] }}"><!-- required="" --> <input type="{{ model_field[1] }}" name="{{ model_field[0] }}" placeholder="{{ model_field[0] }}" aria-label="{{ model_field[0] }}"><!-- required="" -->
{%- endfor %} {%- endfor %}
<button type="submit">Save</button> <button type="submit">Save</button>
<button onclick="history.back()">Cancel</button>
</div> </div>
</form> </form>
{% endblock content %} {% endblock content %}

View File

@ -7,6 +7,7 @@
<input type="text" value="{{ value }}" name="{{ key }}" placeholder="{{ key }}" aria-label="{{ key }}"><!-- required="" --> <input type="text" value="{{ value }}" name="{{ key }}" placeholder="{{ key }}" aria-label="{{ key }}"><!-- required="" -->
{%- endfor %} {%- endfor %}
<button type="submit">Save</button> <button type="submit">Save</button>
<button onclick="history.back()">Cancel</button>
</div> </div>
</form> </form>
{% endblock content %} {% endblock content %}

Binary file not shown.

View File

@ -12,6 +12,9 @@ pub struct Model {
pub comment: String, pub comment: String,
#[sea_orm(column_type = "Text")] #[sea_orm(column_type = "Text")]
pub user: String, pub user: String,
#[sea_orm(column_type = "DateTime")]
#[actix_admin(html_input_type = "datetime-local")]
pub insert_date: DateTimeWithTimeZone
} }
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View File

@ -43,6 +43,7 @@ pub async fn create_post_table(db: &DbConn) -> Result<ExecResult, DbErr> {
) )
.col(ColumnDef::new(comment::Column::Comment).string().not_null()) .col(ColumnDef::new(comment::Column::Comment).string().not_null())
.col(ColumnDef::new(comment::Column::User).string().not_null()) .col(ColumnDef::new(comment::Column::User).string().not_null())
.col(ColumnDef::new(comment::Column::InsertDate).date_time().not_null())
.to_owned(); .to_owned();
create_table(db, &stmt).await create_table(db, &stmt).await