implement macros for active_model generation
This commit is contained in:
parent
88a754eeac
commit
7c8cf72668
@ -23,7 +23,7 @@ serde = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
serde_derive = "1.0.136"
|
||||
quote = "1.0"
|
||||
sea-orm = { version = "0.6.0", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = false }
|
||||
sea-orm = { version = "0.8.0", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = false }
|
||||
syn = "1.0.91"
|
||||
actix_admin = { path = "actix_admin" }
|
||||
azure_auth = { path = "azure_auth" }
|
@ -21,4 +21,4 @@ futures = "0.3.21"
|
||||
serde = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
serde_derive = "1.0.136"
|
||||
sea-orm = { version = "0.6.0", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = false }
|
||||
sea-orm = { version = "0.8.0", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = false }
|
@ -27,4 +27,4 @@ futures = "0.3.21"
|
||||
serde = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
serde_derive = "1.0.136"
|
||||
sea-orm = { version = "0.6.0", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = false }
|
||||
sea-orm = { version = "0.8.0", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = false }
|
@ -8,12 +8,37 @@ use struct_fields::get_fields_for_tokenstream;
|
||||
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)| {
|
||||
let names_const_fields_str = fields
|
||||
.iter()
|
||||
.map(|(_vis, ident, _ty)| {
|
||||
let ident_name = ident.to_string();
|
||||
quote! {
|
||||
#ident_name
|
||||
}
|
||||
});
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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)| !ident.to_string().eq("id"))
|
||||
.map(|(_vis, ident, ty)| {
|
||||
let ident_name = ident.to_string();
|
||||
quote! {
|
||||
#ident: Set(model.get_value::<#ty>(#ident_name).unwrap())
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let fields_for_from_model = fields
|
||||
.iter()
|
||||
.map(|(_vis, ident, _ty)| {
|
||||
let ident_name = ident.to_string();
|
||||
quote! {
|
||||
#ident_name => model.#ident.to_string()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let expanded = quote! {
|
||||
use std::convert::From;
|
||||
@ -25,6 +50,7 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
||||
use sea_orm::{entity::*, query::*};
|
||||
use std::collections::HashMap;
|
||||
use sea_orm::EntityTrait;
|
||||
use quote::quote;
|
||||
|
||||
impl From<Entity> for ActixAdminViewModel {
|
||||
fn from(entity: Entity) -> Self {
|
||||
@ -40,9 +66,10 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
||||
ActixAdminModel {
|
||||
// TODO: create dynamically
|
||||
values: hashmap![
|
||||
"title" => model.title,
|
||||
"text" => model.text,
|
||||
"id" => model.id.to_string()
|
||||
#(#fields_for_from_model),*
|
||||
// "title" => model.title,
|
||||
// "text" => model.text,
|
||||
// "id" => model.id.to_string()
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -50,9 +77,10 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
||||
|
||||
impl From<ActixAdminModel> for ActiveModel {
|
||||
fn from(model: ActixAdminModel) -> Self {
|
||||
ActiveModel {
|
||||
title: Set(model.values.get("title").unwrap().to_string()),
|
||||
text: Set(model.values.get("text").unwrap().to_string()),
|
||||
ActiveModel
|
||||
{
|
||||
#(#fields_for_create_model),*
|
||||
,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@ -71,6 +99,10 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
||||
|
||||
model
|
||||
}
|
||||
|
||||
fn get_entity_name() -> String {
|
||||
Entity.table_name().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -1,11 +1,11 @@
|
||||
use proc_macro2::{Span, Ident};
|
||||
use syn::{
|
||||
Attribute, Fields, Meta, NestedMeta, Visibility, DeriveInput
|
||||
Attribute, Fields, Meta, NestedMeta, Visibility, DeriveInput, Type
|
||||
};
|
||||
|
||||
const ATTR_META_SKIP: &'static str = "skip";
|
||||
|
||||
pub fn get_fields_for_tokenstream(input: proc_macro::TokenStream) -> std::vec::Vec<(syn::Visibility, proc_macro2::Ident)> {
|
||||
pub fn get_fields_for_tokenstream(input: proc_macro::TokenStream) -> std::vec::Vec<(syn::Visibility, proc_macro2::Ident, Type)> {
|
||||
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());
|
||||
@ -32,7 +32,7 @@ pub fn has_skip_attr(attr: &Attribute, path: &'static str) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn filter_fields(fields: &Fields) -> Vec<(Visibility, Ident)> {
|
||||
pub fn filter_fields(fields: &Fields) -> Vec<(Visibility, Ident, Type)> {
|
||||
fields
|
||||
.iter()
|
||||
.filter_map(|field| {
|
||||
@ -45,7 +45,8 @@ pub fn filter_fields(fields: &Fields) -> Vec<(Visibility, Ident)> {
|
||||
{
|
||||
let field_vis = field.vis.clone();
|
||||
let field_ident = field.ident.as_ref().unwrap().clone();
|
||||
Some((field_vis, field_ident))
|
||||
let field_ty = field.ty.to_owned();
|
||||
Some((field_vis, field_ident, field_ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -20,14 +20,6 @@ macro_rules! hashmap {
|
||||
}}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! make_fields {
|
||||
($($element: ident: $ty: ty),*) => {
|
||||
$($element: $ty),*
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// globals
|
||||
lazy_static! {
|
||||
static ref TERA: Tera =
|
||||
@ -70,6 +62,7 @@ pub struct ActixAdminModel {
|
||||
pub trait ActixAdminViewModelTrait {
|
||||
async fn list(db: &DatabaseConnection, page: usize, entities_per_page: usize) -> Vec<ActixAdminModel>;
|
||||
async fn create_entity(db: &DatabaseConnection, model: ActixAdminModel) -> ActixAdminModel;
|
||||
fn get_entity_name() -> String;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
@ -94,8 +87,8 @@ impl ActixAdmin {
|
||||
actix_admin
|
||||
}
|
||||
|
||||
pub fn add_entity(mut self, view_model: &ActixAdminViewModel) -> Self {
|
||||
self.entity_names.push(view_model.entity_name.to_string());
|
||||
pub fn add_entity<E: ActixAdminViewModelTrait>(mut self, view_model: &ActixAdminViewModel) -> Self {
|
||||
self.entity_names.push(E::get_entity_name());
|
||||
let view_model_cloned = view_model.clone();
|
||||
let key = view_model.entity_name.to_string();
|
||||
self.view_models.insert(key, view_model_cloned);
|
||||
@ -124,6 +117,17 @@ impl From<String> for ActixAdminModel {
|
||||
}
|
||||
}
|
||||
|
||||
impl ActixAdminModel {
|
||||
pub fn get_value<T: std::str::FromStr>(&self, key: &str) -> Option<T> {
|
||||
println!("get value for key {}", key);
|
||||
let value = self.values.get(key).unwrap().to_string().parse::<T>();
|
||||
match value {
|
||||
Ok(val) => Some(val),
|
||||
Err(_) => None //panic!("key {} could not be parsed", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn index<T: AppDataTrait>(data: web::Data<T>) -> Result<HttpResponse, Error> {
|
||||
let entity_names = &data.get_actix_admin().entity_names;
|
||||
let mut ctx = Context::new();
|
||||
@ -135,8 +139,8 @@ pub async fn index<T: AppDataTrait>(data: web::Data<T>) -> Result<HttpResponse,
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(body))
|
||||
}
|
||||
|
||||
pub async fn list<T: AppDataTrait, E: ActixAdminViewModelTrait>(req: HttpRequest, data: web::Data<T>, path: web::Path<String>) -> Result<HttpResponse, Error> {
|
||||
let entity_name: String = path.into_inner();
|
||||
pub async fn list<T: AppDataTrait, E: ActixAdminViewModelTrait>(req: HttpRequest, data: web::Data<T>) -> Result<HttpResponse, Error> {
|
||||
let entity_name = E::get_entity_name();
|
||||
let actix_admin = data.get_actix_admin();
|
||||
let view_model: &ActixAdminViewModel = actix_admin.view_models.get(&entity_name).unwrap();
|
||||
let entity_names = &data.get_actix_admin().entity_names;
|
||||
@ -163,10 +167,9 @@ pub async fn list<T: AppDataTrait, E: ActixAdminViewModelTrait>(req: HttpRequest
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(body))
|
||||
}
|
||||
|
||||
pub async fn create_get<T: AppDataTrait, E: ActixAdminViewModelTrait>(_req: HttpRequest, data: web::Data<T>, _body: web::Payload, _text: String, entity_name: web::Path<String>) -> Result<HttpResponse, Error> {
|
||||
pub async fn create_get<T: AppDataTrait, E: ActixAdminViewModelTrait>(_req: HttpRequest, data: web::Data<T>, _body: web::Payload, _text: String) -> Result<HttpResponse, Error> {
|
||||
let _db = &data.get_db();
|
||||
let entity_name: String = entity_name.into_inner();
|
||||
println!("{}", &entity_name);
|
||||
let entity_name = E::get_entity_name();
|
||||
let entity_names = &data.get_actix_admin().entity_names;
|
||||
|
||||
let actix_admin = data.get_actix_admin();
|
||||
@ -184,9 +187,9 @@ pub async fn create_get<T: AppDataTrait, E: ActixAdminViewModelTrait>(_req: Http
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(body))
|
||||
}
|
||||
|
||||
pub async fn create_post<T: AppDataTrait, E: ActixAdminViewModelTrait>(_req: HttpRequest, data: web::Data<T>, text: String, entity_name: web::Path<String>) -> Result<HttpResponse, Error> {
|
||||
pub async fn create_post<T: AppDataTrait, E: ActixAdminViewModelTrait>(_req: HttpRequest, data: web::Data<T>, text: String) -> Result<HttpResponse, Error> {
|
||||
let db = &data.get_db();
|
||||
let entity_name: String = entity_name.into_inner();
|
||||
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();
|
||||
|
BIN
database.db-wal
BIN
database.db-wal
Binary file not shown.
@ -1,9 +1,9 @@
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
//use actix_admin::{ DeriveActixAdminModel };
|
||||
use actix_admin::{ DeriveActixAdminModel };
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize/*, DeriveActixAdminModel*/)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize, DeriveActixAdminModel)]
|
||||
#[sea_orm(table_name = "comment")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
@ -1,10 +1,10 @@
|
||||
// setup
|
||||
use sea_orm::sea_query::{ColumnDef, TableCreateStatement};
|
||||
use sea_orm::{error::*, sea_query, ConnectionTrait, DbConn, ExecResult};
|
||||
pub mod post;
|
||||
pub mod comment;
|
||||
pub use post::Entity as Post;
|
||||
pub mod post;
|
||||
pub use comment::Entity as Comment;
|
||||
pub use post::Entity as Post;
|
||||
|
||||
// setup
|
||||
async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result<ExecResult, DbErr> {
|
||||
@ -23,16 +23,24 @@ pub async fn create_post_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(post::Column::Title).string().not_null())
|
||||
.col(ColumnDef::new(post::Column::Text).string().not_null())
|
||||
.to_owned();
|
||||
|
||||
create_table(db, &stmt).await;
|
||||
|
||||
let stmt = sea_query::Table::create()
|
||||
.table(comment::Entity)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(post::Column::Title)
|
||||
.string()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(post::Column::Text)
|
||||
.string()
|
||||
.not_null(),
|
||||
ColumnDef::new(post::Column::Id)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(comment::Column::Comment).string().not_null())
|
||||
.col(ColumnDef::new(comment::Column::User).string().not_null())
|
||||
.to_owned();
|
||||
|
||||
create_table(db, &stmt).await
|
||||
|
17
src/main.rs
17
src/main.rs
@ -3,7 +3,6 @@ extern crate serde_derive;
|
||||
use actix_admin::{
|
||||
ActixAdmin, ActixAdminViewModel,
|
||||
AppDataTrait as ActixAdminAppDataTrait,
|
||||
ActixAdminViewModelTrait
|
||||
};
|
||||
use actix_session::{CookieSession, Session};
|
||||
use actix_web::{web, App, HttpResponse, HttpServer, middleware};
|
||||
@ -17,7 +16,7 @@ use std::time::Duration;
|
||||
use tera::{Context, Tera};
|
||||
|
||||
mod entity;
|
||||
use entity::{Post};
|
||||
use entity::{Post, Comment};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AppState {
|
||||
@ -63,11 +62,17 @@ fn setup_actix_admin(
|
||||
actix_admin
|
||||
.create_scope::<AppState>()
|
||||
.service(
|
||||
web::scope("/{entity_name}")
|
||||
web::scope("/post")
|
||||
.route("/list", web::get().to(actix_admin::list::<AppState, Post>))
|
||||
.route("/create", web::get().to(actix_admin::create_get::<AppState, Post>))
|
||||
.route("/create", web::post().to(actix_admin::create_post::<AppState, Post>))
|
||||
)
|
||||
.service(
|
||||
web::scope("/comment")
|
||||
.route("/list", web::get().to(actix_admin::list::<AppState, Comment>))
|
||||
.route("/create", web::get().to(actix_admin::create_get::<AppState, Comment>))
|
||||
.route("/create", web::post().to(actix_admin::create_post::<AppState, Comment>))
|
||||
)
|
||||
}
|
||||
|
||||
#[actix_rt::main]
|
||||
@ -105,10 +110,10 @@ async fn main() {
|
||||
let _ = entity::create_post_table(&conn).await;
|
||||
|
||||
let post_view_model = ActixAdminViewModel::from(Post);
|
||||
//let comment_view_model = ActixAdminViewModel::from(Comment);
|
||||
let comment_view_model = ActixAdminViewModel::from(Comment);
|
||||
let actix_admin = ActixAdmin::new()
|
||||
.add_entity(&post_view_model)
|
||||
//.add_entity::<AppState>(&comment_view_model)
|
||||
.add_entity::<Post>(&post_view_model)
|
||||
.add_entity::<Comment>(&comment_view_model)
|
||||
;
|
||||
let app_state = AppState {
|
||||
oauth: client,
|
||||
|
Loading…
Reference in New Issue
Block a user