Write db_type macro
This commit is contained in:
39
Cargo.lock
generated
39
Cargo.lock
generated
@ -5,3 +5,42 @@ version = 4
|
||||
[[package]]
|
||||
name = "libretunes_macro"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
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 = "syn"
|
||||
version = "2.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
@ -7,3 +7,5 @@ edition = "2024"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0.40"
|
||||
syn = { version = "2.0.101", features = ["full"] }
|
||||
|
108
src/lib.rs
Normal file
108
src/lib.rs
Normal file
@ -0,0 +1,108 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{Data, DataStruct, DeriveInput, Fields, FieldsNamed, Path, parse_macro_input};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn db_type(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let derive_input = parse_macro_input!(item as DeriveInput);
|
||||
|
||||
// Get struct
|
||||
let original_struct = if let Data::Struct(data_struct) = derive_input.data.clone() {
|
||||
data_struct
|
||||
} else {
|
||||
return syn::Error::new_spanned(
|
||||
derive_input.ident,
|
||||
"#[db_type] can only be applied to structs",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
};
|
||||
|
||||
// Get fields
|
||||
let fields = if let Fields::Named(fields) = &original_struct.fields {
|
||||
fields.clone()
|
||||
} else {
|
||||
return syn::Error::new_spanned(
|
||||
derive_input.ident,
|
||||
"#[db_type] can only be applied to structs with named fields",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
};
|
||||
|
||||
// Filter out fields with the `#[omit_new]` attribute
|
||||
let new_fields = fields.named.iter().filter(|field| {
|
||||
!field
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|attr| attr.path().is_ident("omit_new"))
|
||||
});
|
||||
|
||||
// Wrap back into FieldsNamed
|
||||
let new_fields = Fields::Named(FieldsNamed {
|
||||
brace_token: fields.brace_token,
|
||||
named: new_fields.cloned().collect(),
|
||||
});
|
||||
|
||||
// Create a new struct with the filtered fields
|
||||
let new_struct = DataStruct {
|
||||
fields: new_fields,
|
||||
..original_struct.clone()
|
||||
};
|
||||
|
||||
// Create a new DeriveInput with the new struct, named "New<OriginalName>"
|
||||
let new_name = format!("New{}", derive_input.ident.clone());
|
||||
let new_derive_input = DeriveInput {
|
||||
ident: syn::Ident::new(&new_name, derive_input.ident.span()),
|
||||
data: Data::Struct(new_struct),
|
||||
..derive_input.clone()
|
||||
};
|
||||
|
||||
// Remove `#[omit_new]` attributes from the original struct
|
||||
let clean_original_fields = fields.named.iter().map(|field| {
|
||||
let mut new_field = field.clone();
|
||||
new_field
|
||||
.attrs
|
||||
.retain(|attr| !attr.path().is_ident("omit_new"));
|
||||
new_field
|
||||
});
|
||||
|
||||
// Wrap back into FieldsNamed
|
||||
let clean_original_fields = Fields::Named(FieldsNamed {
|
||||
brace_token: fields.brace_token,
|
||||
named: clean_original_fields.collect(),
|
||||
});
|
||||
|
||||
// Create a new struct with the cleaned fields
|
||||
let clean_original_struct = DataStruct {
|
||||
fields: clean_original_fields,
|
||||
..original_struct.clone()
|
||||
};
|
||||
|
||||
// Create a new DeriveInput with the cleaned struct
|
||||
let clean_derive_input = DeriveInput {
|
||||
ident: derive_input.ident.clone(),
|
||||
data: Data::Struct(clean_original_struct),
|
||||
..derive_input.clone()
|
||||
};
|
||||
|
||||
// Get the table path from the attribute
|
||||
let table_path = parse_macro_input!(attr as Path);
|
||||
|
||||
// Generate the expanded code
|
||||
let expanded = quote! {
|
||||
#[cfg_attr(feature = "ssr", derive(
|
||||
diesel::prelude::Queryable,
|
||||
diesel::prelude::Selectable,
|
||||
diesel::prelude::Identifiable))]
|
||||
#[cfg_attr(feature = "ssr", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "ssr", diesel(table_name = #table_path))]
|
||||
#clean_derive_input
|
||||
#[cfg_attr(feature = "ssr", derive(diesel::prelude::Insertable))]
|
||||
#[cfg_attr(feature = "ssr", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "ssr", diesel(table_name = #table_path))]
|
||||
#new_derive_input
|
||||
};
|
||||
|
||||
expanded.into()
|
||||
}
|
Reference in New Issue
Block a user