Implement TableMeta proc_macro
This commit is contained in:
		
							parent
							
								
									0d3a89caeb
								
							
						
					
					
						commit
						771be89260
					
				
							
								
								
									
										101
									
								
								persmgr_derive/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								persmgr_derive/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,101 @@
 | 
			
		|||
# This file is automatically @generated by Cargo.
 | 
			
		||||
# It is not intended for manual editing.
 | 
			
		||||
version = 4
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "darling"
 | 
			
		||||
version = "0.13.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "darling_core",
 | 
			
		||||
 "darling_macro",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "darling_core"
 | 
			
		||||
version = "0.13.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "fnv",
 | 
			
		||||
 "ident_case",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "strsim",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "darling_macro"
 | 
			
		||||
version = "0.13.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "darling_core",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fnv"
 | 
			
		||||
version = "1.0.7"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ident_case"
 | 
			
		||||
version = "1.0.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "persmgr_derive"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "darling",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "proc-macro2"
 | 
			
		||||
version = "1.0.101"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "unicode-ident",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "quote"
 | 
			
		||||
version = "1.0.40"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "strsim"
 | 
			
		||||
version = "0.10.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "syn"
 | 
			
		||||
version = "1.0.109"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "unicode-ident",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "unicode-ident"
 | 
			
		||||
version = "1.0.19"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
 | 
			
		||||
							
								
								
									
										13
									
								
								persmgr_derive/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								persmgr_derive/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
[package]
 | 
			
		||||
name = "persmgr_derive"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2024"
 | 
			
		||||
 | 
			
		||||
[lib]
 | 
			
		||||
proc-macro = true
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
quote = "1"
 | 
			
		||||
proc-macro2 = "1"
 | 
			
		||||
syn = { version = "1", features = ["full"] }
 | 
			
		||||
darling = "0.13"
 | 
			
		||||
							
								
								
									
										109
									
								
								persmgr_derive/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								persmgr_derive/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,109 @@
 | 
			
		|||
use darling::FromDeriveInput;
 | 
			
		||||
use proc_macro::{self, TokenStream};
 | 
			
		||||
use quote::quote;
 | 
			
		||||
use syn::{Data, DeriveInput, Expr, Fields, Type, parse_macro_input};
 | 
			
		||||
 | 
			
		||||
#[derive(FromDeriveInput, Default)]
 | 
			
		||||
#[darling(default, attributes(meta))]
 | 
			
		||||
struct Opts {
 | 
			
		||||
    table: String,
 | 
			
		||||
    key_t: Option<Expr>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[proc_macro_derive(TableMeta, attributes(meta))]
 | 
			
		||||
pub fn derive_table_meta(input: TokenStream) -> TokenStream {
 | 
			
		||||
    let input = parse_macro_input!(input);
 | 
			
		||||
    let opts = Opts::from_derive_input(&input)
 | 
			
		||||
        .expect("Expected attribute #[meta(table=\"users\", key_t=i32)]");
 | 
			
		||||
 | 
			
		||||
    let mut struct_items = Vec::new();
 | 
			
		||||
 | 
			
		||||
    if let Data::Struct(data) = &input.data {
 | 
			
		||||
        if let Fields::Named(fields) = &data.fields {
 | 
			
		||||
            for field in &fields.named {
 | 
			
		||||
                let ident = field.ident.as_ref().unwrap();
 | 
			
		||||
                let s = ident.to_string();
 | 
			
		||||
                if s.as_str() == "id" || s.as_str() == "created_at" {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                let mut should_deref = false;
 | 
			
		||||
                if let Type::Path(tp) = &field.ty {
 | 
			
		||||
                    if tp.path.segments.first().unwrap().ident == "ForeignKey" {
 | 
			
		||||
                        should_deref = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                struct_items.push((ident, should_deref));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let ident = input.ident;
 | 
			
		||||
    let table_name = opts.table;
 | 
			
		||||
    let key_t = opts
 | 
			
		||||
        .key_t
 | 
			
		||||
        .unwrap_or_else(|| syn::parse_str::<Expr>("i64").unwrap());
 | 
			
		||||
 | 
			
		||||
    // dbg!(&table_name);
 | 
			
		||||
    //dbg!(&key_t);
 | 
			
		||||
 | 
			
		||||
    let mut query = format!("UPDATE {table_name} SET ");
 | 
			
		||||
    let mut query_args = quote!(self.id);
 | 
			
		||||
    let mut i = 2;
 | 
			
		||||
    let mut modified_update = false;
 | 
			
		||||
    for (item, should_deref) in &struct_items {
 | 
			
		||||
        let s = item.to_string();
 | 
			
		||||
        //dbg!(&s);
 | 
			
		||||
 | 
			
		||||
        if s.as_str() == "modified_at" {
 | 
			
		||||
            modified_update = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if i != 2 {
 | 
			
		||||
            query.push_str(", ");
 | 
			
		||||
        }
 | 
			
		||||
        query.push_str(s.as_str());
 | 
			
		||||
        query.push_str(&format!(" = ${i} "));
 | 
			
		||||
        i += 1;
 | 
			
		||||
 | 
			
		||||
        let deref = if *should_deref { quote!(*) } else { quote!() };
 | 
			
		||||
 | 
			
		||||
        query_args = quote! {
 | 
			
		||||
            #query_args, #deref self.#item
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    query.push_str("WHERE id = $1 RETURNING *");
 | 
			
		||||
 | 
			
		||||
    let mut modified_at = quote! {};
 | 
			
		||||
 | 
			
		||||
    if modified_update {
 | 
			
		||||
        modified_at = quote! {
 | 
			
		||||
            self.modified_at = time::OffsetDateTime::now_utc().unix_timestamp();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let output = quote! {
 | 
			
		||||
        impl TableMeta for #ident {
 | 
			
		||||
            type PrimaryKey = #key_t;
 | 
			
		||||
            const TABLE: &'static str = #table_name;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl #ident {
 | 
			
		||||
            pub async fn update(&mut self, pool: &crate::db::CurrPool) -> anyhow::Result<Self> {
 | 
			
		||||
                #modified_at
 | 
			
		||||
                let session = sqlx::query_as!(
 | 
			
		||||
                    #ident,
 | 
			
		||||
                    #query,
 | 
			
		||||
                    #query_args
 | 
			
		||||
                )
 | 
			
		||||
                .fetch_one(pool)
 | 
			
		||||
                .await?;
 | 
			
		||||
                Ok(session)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    eprintln!("{}", output.to_string());
 | 
			
		||||
 | 
			
		||||
    output.into()
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user