From 96b069042e1205b44925e7fa0c22c6c2c5df0fe5 Mon Sep 17 00:00:00 2001 From: manuel Date: Mon, 6 Jun 2022 15:25:37 +0200 Subject: [PATCH] separate primary_key from other field values --- .../actix_admin_macros/src/attributes.rs | 3 +- actix_admin/actix_admin_macros/src/lib.rs | 90 +------- .../actix_admin_macros/src/model_fields.rs | 18 ++ .../actix_admin_macros/src/struct_fields.rs | 192 +++++++++++++----- actix_admin/src/model.rs | 4 +- actix_admin/src/routes/create_post.rs | 4 +- actix_admin/src/routes/edit_post.rs | 4 +- actix_admin/src/routes/list.rs | 1 - actix_admin/src/view_model.rs | 1 + actix_admin/templates/list.html | 6 +- azure_auth/src/lib.rs | 1 - database.db-wal | Bin 37112 -> 61832 bytes src/entity/comment.rs | 1 + src/entity/mod.rs | 2 +- src/entity/post.rs | 3 +- 15 files changed, 186 insertions(+), 144 deletions(-) create mode 100644 actix_admin/actix_admin_macros/src/model_fields.rs diff --git a/actix_admin/actix_admin_macros/src/attributes.rs b/actix_admin/actix_admin_macros/src/attributes.rs index 554ef53..4d45519 100644 --- a/actix_admin/actix_admin_macros/src/attributes.rs +++ b/actix_admin/actix_admin_macros/src/attributes.rs @@ -9,7 +9,8 @@ pub mod derive_attr { Default )] pub struct ActixAdmin { - pub inner_type: Option, + pub primary_key: Option<()> + //pub inner_type: Option, // Anything that implements `syn::parse::Parse` is supported. //mandatory_type: syn::Type, diff --git a/actix_admin/actix_admin_macros/src/lib.rs b/actix_admin/actix_admin_macros/src/lib.rs index b196122..df418a2 100644 --- a/actix_admin/actix_admin_macros/src/lib.rs +++ b/actix_admin/actix_admin_macros/src/lib.rs @@ -2,7 +2,9 @@ use proc_macro; use quote::quote; mod struct_fields; -use struct_fields::get_fields_for_tokenstream; +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}; + +mod model_fields; mod attributes; @@ -10,82 +12,12 @@ mod attributes; pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let fields = get_fields_for_tokenstream(input); - let names_const_fields_str = fields - .iter() - .map(|(_vis, ident, _ty, _is_option)| { - let ident_name = ident.to_string(); - quote! { - #ident_name - } - }) - .collect::>(); - - let fields_for_create_model = fields - .iter() - // TODO: filter id attr based on struct attr or sea_orm primary_key attr - .filter(|(_vis, ident, _ty, _is_option)| !ident.to_string().eq("id")) - .map(|(_vis, ident, ty, is_option)| { - let ident_name = ident.to_string(); - match is_option { - true => { - quote! { - #ident: Set(model.get_value::<#ty>(#ident_name)) - } - }, - false => { - quote! { - #ident: Set(model.get_value::<#ty>(#ident_name).unwrap()) - } - } - } - }) - .collect::>(); - - let fields_for_edit_model = fields - .iter() - // TODO: filter id attr based on struct attr or sea_orm primary_key attr - .filter(|(_vis, ident, _ty, _is_option)| !ident.to_string().eq("id")) - .map(|(_vis, ident, ty, is_option)| { - let ident_name = ident.to_string(); - println!("edit {} {:?}", &ident_name, ty); - match is_option { - true => { - quote! { - entity.#ident = Set(model.get_value::<#ty>(#ident_name)) - } - }, - false => { - quote! { - entity.#ident = Set(model.get_value::<#ty>(#ident_name).unwrap()) - } - } - } - }) - .collect::>(); - - let fields_for_from_model = fields - .iter() - .map(|(_vis, ident, _ty, is_option)| { - let ident_name = ident.to_string(); - println!("from {} {:?}", &ident_name, _ty); - - match is_option { - true => { - quote! { - #ident_name => match model.#ident { - Some(val) => val.to_string(), - None => "".to_owned() - } - } - }, - false => { - quote! { - #ident_name => model.#ident.to_string() - } - } - } - }) - .collect::>(); + let names_const_fields_str = get_field_names(&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_edit_model = get_fields_for_edit_model(&fields); + let fields_for_from_model = get_fields_for_from_model(&fields); + let field_for_primary_key = get_field_for_primary_key(&fields); let expanded = quote! { use std::convert::From; @@ -102,6 +34,7 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea impl From for ActixAdminViewModel { fn from(entity: Entity) -> Self { ActixAdminViewModel { + primary_key: #name_primary_field_str.to_string(), entity_name: entity.table_name().to_string(), fields: Entity::get_fields() } @@ -111,6 +44,7 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea impl From for ActixAdminModel { fn from(model: Model) -> Self { ActixAdminModel { + #field_for_primary_key, values: hashmap![ #(#fields_for_from_model),* ] @@ -139,8 +73,6 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea async fn create_entity(db: &DatabaseConnection, mut model: ActixAdminModel) -> ActixAdminModel { let new_model = ActiveModel::from(model.clone()); let insert_operation = Entity::insert(new_model).exec(db).await; - println!("creating {:?}", model); - println!("operation {:?}", insert_operation); model } diff --git a/actix_admin/actix_admin_macros/src/model_fields.rs b/actix_admin/actix_admin_macros/src/model_fields.rs new file mode 100644 index 0000000..1e09023 --- /dev/null +++ b/actix_admin/actix_admin_macros/src/model_fields.rs @@ -0,0 +1,18 @@ +use syn::{ + Visibility, Type +}; + +pub struct ModelField { + pub visibility: Visibility, + pub ident: proc_macro2::Ident, + pub ty: Type, + // struct field is option<> + pub inner_type: Option, + pub primary_key: bool +} + +impl ModelField { + pub fn is_option(&self) -> bool { + self.inner_type.is_some() + } +} \ 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 6ebe145..331348f 100644 --- a/actix_admin/actix_admin_macros/src/struct_fields.rs +++ b/actix_admin/actix_admin_macros/src/struct_fields.rs @@ -1,13 +1,12 @@ -use proc_macro2::{Span, Ident}; +use proc_macro2::{Span, Ident, TokenStream}; use syn::{ - Attribute, Fields, Meta, NestedMeta, Visibility, DeriveInput, Type + Fields, DeriveInput }; - +use quote::quote; use crate::attributes::derive_attr; +use crate::model_fields::{ ModelField }; -const ACTIX_ADMIN: &'static str = "actix_admin"; - -pub fn get_fields_for_tokenstream(input: proc_macro::TokenStream) -> std::vec::Vec<(syn::Visibility, proc_macro2::Ident, Type, bool)> { +pub fn get_fields_for_tokenstream(input: proc_macro::TokenStream) -> std::vec::Vec { let ast: DeriveInput = syn::parse(input).unwrap(); let (_vis, ty, _generics) = (&ast.vis, &ast.ident, &ast.generics); let _names_struct_ident = Ident::new(&(ty.to_string() + "FieldStaticStr"), Span::call_site()); @@ -19,58 +18,29 @@ pub fn get_fields_for_tokenstream(input: proc_macro::TokenStream) -> std::vec::V fields } -pub fn has_skip_attr(attr: &Attribute, path: &'static str) -> bool { - if let Ok(Meta::List(meta_list)) = attr.parse_meta() { - //println!("1"); - //println!("{:?}", meta_list.path); - //println!("{}", path); - if meta_list.path.is_ident(path) { - //println!("2"); - for nested_item in meta_list.nested.iter() { - if let NestedMeta::Meta(Meta::Path(path)) = nested_item { - //println!("3"); - if path.is_ident(ACTIX_ADMIN) { - //println!("true"); - return true; - } - } - } - } - } - false -} - -pub fn get_field_type<'a>(actix_admin_attr: &'a Option, field: &'a syn::Field) -> &'a syn::Type { - match actix_admin_attr { - Some(attr) => { - match &attr.inner_type { - Some(inner_type) => &inner_type, - None => &field.ty - } - }, - _ => &field.ty - } -} - -pub fn filter_fields(fields: &Fields) -> Vec<(Visibility, Ident, Type, bool)> { +pub fn filter_fields(fields: &Fields) -> Vec { fields .iter() .filter_map(|field| { let actix_admin_attr = derive_attr::ActixAdmin::try_from_attributes(&field.attrs).unwrap_or_default(); - if field - .attrs - .iter() - .find(|attr| has_skip_attr(attr, ACTIX_ADMIN)) - .is_none() - && field.ident.is_some() + if field.ident.is_some() { let field_vis = field.vis.clone(); let field_ident = field.ident.as_ref().unwrap().clone(); - println!("{}", field_ident.to_string()); - let is_option = extract_type_from_option(&field.ty).is_some(); - let field_ty = get_field_type(&actix_admin_attr, &field).to_owned(); - Some((field_vis, field_ident, field_ty, is_option)) + let inner_type = extract_type_from_option(&field.ty); + let field_ty = field.ty.to_owned(); + let is_primary_key = actix_admin_attr.map_or(false, |attr| attr.primary_key.is_some()); + + let model_field = ModelField { + visibility: field_vis, + ident: field_ident, + ty: field_ty, + inner_type: inner_type, + primary_key: is_primary_key + }; + + Some(model_field) } else { None } @@ -78,7 +48,7 @@ pub fn filter_fields(fields: &Fields) -> Vec<(Visibility, Ident, Type, bool)> { .collect::>() } -fn extract_type_from_option(ty: &syn::Type) -> Option<&syn::Type> { +fn extract_type_from_option(ty: &syn::Type) -> Option { use syn::{GenericArgument, Path, PathArguments, PathSegment}; fn extract_type_path(ty: &syn::Type) -> Option<&Path> { @@ -117,7 +87,125 @@ fn extract_type_from_option(ty: &syn::Type) -> Option<&syn::Type> { } }) .and_then(|generic_arg| match *generic_arg { - GenericArgument::Type(ref ty) => Some(ty), + GenericArgument::Type(ref ty) => Some(ty.to_owned()), _ => None, }) +} + +pub fn get_field_names(fields: &Vec) -> Vec { + fields + .iter() + .filter(|model_field| !model_field.primary_key) + .map(|model_field| { + let ident_name = model_field.ident.to_string(); + quote! { + #ident_name + } + }) + .collect::>() +} + +pub fn get_field_for_primary_key(fields: &Vec) -> TokenStream { + let primary_key_model_field = fields + .iter() + // TODO: filter id attr based on struct attr or sea_orm primary_key attr + .find(|model_field| model_field.primary_key) + .expect("model must have a single primary key"); + + let ident = primary_key_model_field.ident.to_owned(); + + quote! { + primary_key: Some(model.#ident.to_string()) + } +} + +pub fn get_primary_key_field_name(fields: &Vec) -> String { + let primary_key_model_field = fields + .iter() + // TODO: filter id attr based on struct attr or sea_orm primary_key attr + .find(|model_field| model_field.primary_key) + .expect("model must have a single primary key"); + + primary_key_model_field.ident.to_string() +} + +pub fn get_fields_for_from_model(fields: &Vec) -> Vec { + fields + .iter() + .filter(|model_field| !model_field.primary_key) + .map(|model_field| { + let ident_name = model_field.ident.to_string(); + let ident = model_field.ident.to_owned(); + + match model_field.is_option() { + true => { + quote! { + #ident_name => match model.#ident { + Some(val) => val.to_string(), + None => "".to_owned() + } + } + }, + false => { + quote! { + #ident_name => model.#ident.to_string() + } + } + } + }) + .collect::>() +} + +pub fn get_fields_for_create_model(fields: &Vec) -> Vec { + fields + .iter() + // TODO: filter id attr based on struct attr or sea_orm primary_key attr + .filter(|model_field| !model_field.primary_key) + .map(|model_field| { + let ident_name = model_field.ident.to_string(); + let ident = model_field.ident.to_owned(); + let ty = model_field.ty.to_owned(); + + match model_field.is_option() { + true => { + let inner_ty = model_field.inner_type.to_owned().unwrap(); + quote! { + #ident: Set(model.get_value::<#inner_ty>(#ident_name)) + } + }, + false => { + quote! { + #ident: Set(model.get_value::<#ty>(#ident_name).unwrap()) + } + } + } + }) + .collect::>() +} + +pub fn get_fields_for_edit_model(fields: &Vec) -> Vec { + fields + .iter() + // TODO: filter id attr based on struct attr or sea_orm primary_key attr + .filter(|model_field| !model_field.primary_key) + .map(|model_field| { + let ident_name = model_field.ident.to_string(); + let ident = model_field.ident.to_owned(); + let ty = model_field.ty.to_owned(); + + match model_field.is_option() { + true => { + let inner_ty = model_field.inner_type.to_owned().unwrap(); + quote! { + entity.#ident = Set(model.get_value::<#inner_ty>(#ident_name)) + } + }, + false => { + quote! { + entity.#ident = Set(model.get_value::<#ty>(#ident_name).unwrap()) + } + } + } + }) + .collect::>() } \ No newline at end of file diff --git a/actix_admin/src/model.rs b/actix_admin/src/model.rs index 1bf7a13..15ec06e 100644 --- a/actix_admin/src/model.rs +++ b/actix_admin/src/model.rs @@ -17,6 +17,7 @@ pub trait ActixAdminModelTrait { #[derive(Clone, Debug, Serialize)] pub struct ActixAdminModel { + pub primary_key: Option, pub values: HashMap, } @@ -32,13 +33,12 @@ impl From for ActixAdminModel { ); } - ActixAdminModel { values: hashmap } + ActixAdminModel { primary_key: None, values: hashmap } } } impl ActixAdminModel { pub fn get_value(&self, key: &str) -> Option { - println!("get value for key {}", key); let value = self.values.get(key).unwrap().to_string().parse::(); match value { Ok(val) => Some(val), diff --git a/actix_admin/src/routes/create_post.rs b/actix_admin/src/routes/create_post.rs index f4bb237..d68c296 100644 --- a/actix_admin/src/routes/create_post.rs +++ b/actix_admin/src/routes/create_post.rs @@ -12,8 +12,8 @@ pub async fn create_post let entity_name = E::get_entity_name(); let actix_admin = data.get_actix_admin(); let view_model = actix_admin.view_models.get(&entity_name).unwrap(); - let mut admin_model = ActixAdminModel::from(text); - admin_model = E::create_entity(db, admin_model).await; + let mut _admin_model = ActixAdminModel::from(text); + _admin_model = E::create_entity(db, _admin_model).await; Ok(HttpResponse::Found() .append_header(( diff --git a/actix_admin/src/routes/edit_post.rs b/actix_admin/src/routes/edit_post.rs index 25f4c33..e62388c 100644 --- a/actix_admin/src/routes/edit_post.rs +++ b/actix_admin/src/routes/edit_post.rs @@ -13,8 +13,8 @@ pub async fn edit_post( let entity_name = E::get_entity_name(); let actix_admin = data.get_actix_admin(); let view_model = actix_admin.view_models.get(&entity_name).unwrap(); - let mut admin_model = ActixAdminModel::from(text); - admin_model = E::edit_entity(db, id.into_inner(), admin_model).await; + let mut _admin_model = ActixAdminModel::from(text); + _admin_model = E::edit_entity(db, id.into_inner(), _admin_model).await; Ok(HttpResponse::Found() .append_header(( diff --git a/actix_admin/src/routes/list.rs b/actix_admin/src/routes/list.rs index a72c255..2143dd9 100644 --- a/actix_admin/src/routes/list.rs +++ b/actix_admin/src/routes/list.rs @@ -37,7 +37,6 @@ pub async fn list( let result: (usize, Vec) = E::list(db, page, entities_per_page).await; let entities = result.1; let num_pages = result.0; - println!("{:?}", entities); let mut ctx = Context::new(); ctx.insert("entity_names", &entity_names); diff --git a/actix_admin/src/view_model.rs b/actix_admin/src/view_model.rs index 3ef111c..e704514 100644 --- a/actix_admin/src/view_model.rs +++ b/actix_admin/src/view_model.rs @@ -25,5 +25,6 @@ pub trait ActixAdminViewModelTrait { #[derive(Clone, Debug, Serialize)] pub struct ActixAdminViewModel { pub entity_name: String, + pub primary_key: String, pub fields: Vec<(String, ActixAdminField)>, } \ No newline at end of file diff --git a/actix_admin/templates/list.html b/actix_admin/templates/list.html index a1312dd..0d763d1 100644 --- a/actix_admin/templates/list.html +++ b/actix_admin/templates/list.html @@ -5,6 +5,7 @@ + {% for model_field in view_model.fields -%} {%- endfor %} @@ -17,12 +18,13 @@ {% for entity in entities -%} + {% for model_field in view_model.fields -%} {%- endfor %} {%- endfor %} diff --git a/azure_auth/src/lib.rs b/azure_auth/src/lib.rs index 1e0deed..591df12 100644 --- a/azure_auth/src/lib.rs +++ b/azure_auth/src/lib.rs @@ -125,7 +125,6 @@ async fn read_user(api_base_url: &str, access_token: &AccessToken) -> UserInfo { Err(e) => panic!("Invalid UTF-8 sequence: {}", e), }; - println!("{} {}", &resp.status_code, s); serde_json::from_slice(&resp.body).unwrap() } diff --git a/database.db-wal b/database.db-wal index 6c52d747fe9dc529494d7aee3259275ea9f06063..3e9d90224d7b771e6507853182fbcdb195753f43 100644 GIT binary patch delta 399 zcmeydkg4M_^M)T2R2Uc-n1Gm}@}hBu(Vj~!cb`@tjE<~W&kK^^Kgz)Wi2vy3M1h_B zveHa!4C3O-3?-?>B}N&kIXU^RWvNA#DT$RKsfm+sO^}Hl|x~*5>S8r310?0LaDzKs{Udl@vfWDk9sc zotlzaf@Uqu3vdHl%wEj4im3C2IN=e@!0r6 Result { .col(ColumnDef::new(post::Column::TeaOptional).string()) .to_owned(); - create_table(db, &stmt).await; + let _result = create_table(db, &stmt).await; let stmt = sea_query::Table::create() .table(comment::Entity) diff --git a/src/entity/post.rs b/src/entity/post.rs index e69c7ec..8e6c18b 100644 --- a/src/entity/post.rs +++ b/src/entity/post.rs @@ -10,12 +10,13 @@ use std::fmt::Display; pub struct Model { #[sea_orm(primary_key)] #[serde(skip_deserializing)] + #[actix_admin(primary_key)] pub id: i32, pub title: String, #[sea_orm(column_type = "Text")] pub text: String, pub tea_mandatory: Tea, - #[actix_admin(inner_type=Tea)] + #[actix_admin()] pub tea_optional: Option, }
{{ view_model.primary_key }}{{ model_field[0] }}
{{ entity.primary_key }}{{ entity.values | get(key=model_field[0]) }} - - 🗑 + + 🗑