add derive + docs

This commit is contained in:
JMARyA 2024-07-17 16:49:35 +02:00
parent 5da65fb603
commit ca2b0036f0
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
11 changed files with 400 additions and 43 deletions

54
mongod_derive/Cargo.lock generated Normal file
View 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
View 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
View 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)
}