leveldb_orm_derive/
lib.rs

1//! Use `LeveldbOrm` + `leveldb_key` to auto impl trait in [leveldb-orm](https://crates.io/crates/leveldb-orm)
2//!
3//! ```rust
4//! use leveldb_orm::LeveldbOrm;
5//!
6//! #[derive(LeveldbOrm)]
7//! #[leveldb_key(executable, args)]
8//! struct Command {
9//!     pub executable: u8,
10//!     pub args: Vec<String>,
11//!     pub current_dir: Option<String>,
12//! }
13//!  
14//! // Generate code
15//!
16//! // impl<'a> leveldb_orm::KeyOrm<'a> for Command {
17//! //     type KeyType = (u8, Vec<String>);
18//! //     type KeyTypeRef = (&'a u8, &'a Vec<String>);
19//! //     #[inline]
20//! //     fn key(
21//! //         &self,
22//! //     ) -> std::result::Result<leveldb_orm::EncodedKey<Self>, Box<dyn std::error::Error>> {
23//! //         Self::encode_key((&self.executable, &self.args))
24//! //     }
25//! // }
26//! ```
27
28use proc_macro::TokenStream;
29use quote::quote;
30use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, Error, Result};
31
32#[proc_macro_derive(LeveldbOrm, attributes(leveldb_key))]
33pub fn derive(input: TokenStream) -> TokenStream {
34    let input = parse_macro_input!(input as DeriveInput);
35    derive_orm(input).map_or_else(|e| TokenStream::from(e.to_compile_error()), |r| r)
36}
37
38fn derive_orm(input: DeriveInput) -> Result<TokenStream> {
39    let span = input.span();
40    let fields = if let Data::Struct(data_struct) = input.data {
41        data_struct.fields
42    } else {
43        return Err(Error::new(span, "LeveldbOrm macro for Struct only"));
44    };
45
46    let mut keys = parse::parse_leveldb_key(&input.attrs)
47        .ok_or_else(|| Error::new(span, "Need attributr: `#[leveldb_key(key1, key2, ...)]`"))?;
48    let mut key_types = parse::parse_key_types(&keys, &fields)?;
49
50    let (keys, key_types, key_types_ref) = if key_types.len() == 1 {
51        let key = keys.pop().unwrap();
52        let key_type = key_types.pop().unwrap();
53        (
54            quote! { &self.#key },
55            quote! {#key_type},
56            quote! {&'a #key_type},
57        )
58    } else {
59        (
60            quote! { (#(&self.#keys,)*) },
61            quote! {(#(#key_types,)*)},
62            quote! {(#(&'a #key_types,)*)},
63        )
64    };
65    let ident = input.ident;
66
67    let res = quote! {
68        impl<'a> leveldb_orm::KeyOrm<'a> for #ident {
69            type KeyType = #key_types;
70            type KeyTypeRef = #key_types_ref;
71
72            #[inline]
73            fn key(&self) -> leveldb_orm::Result<leveldb_orm::EncodedKey<Self>> {
74                Self::encode_key(#keys)
75            }
76        }
77    };
78    Ok(res.into())
79}
80
81mod parse {
82    use syn::{Attribute, Error, Fields, Ident, Result, Type};
83
84    pub fn parse_leveldb_key(attrs: &[Attribute]) -> Option<Vec<Ident>> {
85        attrs.iter().find_map(|attr| {
86            if attr.path().is_ident("leveldb_key") {
87                let mut keys = vec![];
88                attr.parse_nested_meta(|meta| {
89                    if let Some(key) = meta.path.get_ident() {
90                        keys.push(key.clone());
91                    }
92                    Ok(())
93                })
94                .unwrap();
95                (!keys.is_empty()).then_some(keys)
96            } else {
97                None
98            }
99        })
100    }
101
102    pub fn parse_key_types(keys: &[Ident], fields: &Fields) -> Result<Vec<Type>> {
103        let mut res = vec![];
104        for key in keys {
105            let ty = fields
106                .iter()
107                .find_map(|field| {
108                    field
109                        .ident
110                        .as_ref()
111                        .filter(|ident| ident == &key)
112                        .map(|_| field.ty.clone())
113                })
114                .ok_or_else(|| {
115                    Error::new(
116                        key.span(),
117                        format!("leveldb_key: \"{key}\" not found in struct.",),
118                    )
119                })?;
120            res.push(ty);
121        }
122        Ok(res)
123    }
124}