add derive + docs
This commit is contained in:
parent
5da65fb603
commit
ca2b0036f0
11 changed files with 400 additions and 43 deletions
54
mongod_derive/Cargo.lock
generated
Normal file
54
mongod_derive/Cargo.lock
generated
Normal file
|
@ -0,0 +1,54 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "case"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c"
|
||||
|
||||
[[package]]
|
||||
name = "mongod_derive"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[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.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
13
mongod_derive/Cargo.toml
Normal file
13
mongod_derive/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "mongod_derive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
case = "1.0.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
135
mongod_derive/src/lib.rs
Normal file
135
mongod_derive/src/lib.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
extern crate proc_macro;
|
||||
use case::CaseExt;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, Data, DeriveInput, Fields, Type, TypePath};
|
||||
|
||||
/// Get inner type. Example: Returns `T` for `Option<T>`.
|
||||
fn extract_inner_type<'a>(ty: &'a Type, parent: &'a str) -> Option<&'a Type> {
|
||||
if let Type::Path(type_path) = ty {
|
||||
if type_path.path.segments.len() == 1 {
|
||||
let segment = &type_path.path.segments[0];
|
||||
if segment.ident == parent {
|
||||
if let syn::PathArguments::AngleBracketed(ref args) = segment.arguments {
|
||||
if args.args.len() == 1 {
|
||||
if let syn::GenericArgument::Type(ref inner_type) = args.args[0] {
|
||||
return Some(inner_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn type_path(ty: &syn::Type) -> TypePath {
|
||||
if let syn::Type::Path(type_path) = ty {
|
||||
return type_path.clone();
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn is_type(ty: &syn::Type, t: &str) -> bool {
|
||||
let type_path = type_path(ty);
|
||||
let id = type_path.path.segments.first().unwrap().ident.to_string();
|
||||
id == t
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Model)]
|
||||
pub fn model_derive(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let name = input.ident;
|
||||
|
||||
// Generate code for each field
|
||||
let field_code = if let Data::Struct(data_struct) = input.data {
|
||||
match data_struct.fields {
|
||||
Fields::Named(fields_named) => {
|
||||
let field_process_code: Vec<_> = fields_named.named.iter().map(|field| {
|
||||
let field_name = &field.ident.as_ref().unwrap();
|
||||
let field_type = &field.ty;
|
||||
let field_name_str = field_name.to_string();
|
||||
|
||||
if field_name_str == "_id" {
|
||||
return quote! {};
|
||||
}
|
||||
|
||||
if is_type(field_type, "Historic") {
|
||||
let inner_field_type = extract_inner_type(field_type, "Historic").unwrap();
|
||||
if is_type(inner_field_type, "Vec") {
|
||||
return quote! {
|
||||
mongod::update_historic_vec!(self, obj, #field_name_str, #field_name, update);
|
||||
};
|
||||
}
|
||||
|
||||
return quote! {
|
||||
mongod::update_historic_str!(self, obj, #field_name_str, #field_name, update);
|
||||
}
|
||||
}
|
||||
|
||||
if is_type(field_type, "Option") {
|
||||
let inner_field_type = extract_inner_type(field_type, "Option").unwrap();
|
||||
|
||||
if is_type(inner_field_type, "Historic") {
|
||||
let sub_inner_field_type = extract_inner_type(inner_field_type, "Historic").unwrap();
|
||||
if is_type(sub_inner_field_type, "Reference") {
|
||||
return quote! {
|
||||
mongod::update_historic_ref_option!(self, obj, #field_name_str, #field_name, update);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return quote! {
|
||||
mongod::update_value_option!(self, obj, #field_name_str, #field_name, update, #inner_field_type);
|
||||
};
|
||||
}
|
||||
|
||||
quote! {
|
||||
mongod::update_value!(self, obj, #field_name_str, #field_name, update, #field_type);
|
||||
}
|
||||
}).collect();
|
||||
|
||||
quote! {
|
||||
impl mongod::model::Model for #name {
|
||||
async fn update_values(
|
||||
&mut self,
|
||||
obj: &serde_json::Map<String, serde_json::Value>,
|
||||
update: &mut mongodb::bson::Document,
|
||||
) {
|
||||
#( #field_process_code )*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
} else {
|
||||
unimplemented!()
|
||||
};
|
||||
|
||||
TokenStream::from(field_code)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Referencable)]
|
||||
pub fn referencable_derive(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let name = input.ident;
|
||||
let name_str = name.to_string().to_snake();
|
||||
|
||||
let code = quote! {
|
||||
impl mongod::model::reference::Referencable for MyStruct {
|
||||
fn collection_name() -> &'static str {
|
||||
#name_str
|
||||
}
|
||||
|
||||
fn id(&self) -> &str {
|
||||
&self._id
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(code)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue