rustling_derive/
lib.rs

1//! # rustling-derive ⚙️
2//!
3//! Procedural macros for automatic repository generation in the **Rustling ORM** ecosystem.
4//!
5//! This crate provides convenient `#[derive(...)]` macros that automatically
6//! implement repository and entity patterns for MongoDB and PostgreSQL.
7//!
8//! ## ✨ Available Macros
9//!
10//! - `#[derive(Repository)]` — derive a PostgreSQL repository implementation
11//! - `#[derive(MongoRepository)]` — derive a MongoDB repository implementation
12//! - `#[derive(Entity)]` — derive helper methods for SQL entities (columns & values)
13//!
14//! ## 💡 Example
15//! ```rust,no_run
16//! use rustling_derive::{Entity, Repository};
17//!
18//! #[derive(Entity)]
19//! struct User {
20//!     id: i32,
21//!     name: String,
22//!     email: String,
23//! }
24//!
25//! #[derive(Repository)]
26//! #[entity(User)]
27//! #[id(i32)]
28//! #[table("users")]
29//! struct UserRepository;
30//! ```
31//!
32//! See the [crate README](https://crates.io/crates/rustling-derive) for setup instructions.
33
34extern crate proc_macro;
35
36use proc_macro::TokenStream;
37use quote::quote;
38use syn::{Data, DeriveInput, Fields, parse_macro_input};
39
40mod common;
41
42#[cfg(feature = "mongo")]
43mod mongo_macro;
44#[cfg(feature = "postgres")]
45mod postgres_macro;
46
47#[cfg(feature = "postgres")]
48#[proc_macro_derive(Repository, attributes(entity, id, table))]
49pub fn repository_derive(input: TokenStream) -> TokenStream {
50    postgres_macro::repository_derive(input)
51}
52
53#[cfg(feature = "mongo")]
54#[proc_macro_derive(MongoRepository, attributes(entity, id, collection))]
55pub fn mongo_repository_derive(input: TokenStream) -> TokenStream {
56    mongo_macro::mongo_repository_derive(input)
57}
58
59#[proc_macro_derive(Entity)]
60pub fn derive_entity(input: TokenStream) -> TokenStream {
61    let ast = parse_macro_input!(input as DeriveInput);
62    let name = &ast.ident;
63
64    let fields: Vec<_> = match &ast.data {
65        Data::Struct(data) => match &data.fields {
66            Fields::Named(fields_named) => fields_named
67                .named
68                .iter()
69                .filter(|f| f.ident.as_ref().unwrap() != "id")
70                .collect(),
71            _ => panic!("Entity derive only supports named fields"),
72        },
73        _ => panic!("Entity derive only supports structs"),
74    };
75
76    let column_names: Vec<_> = fields
77        .iter()
78        .map(|f| f.ident.as_ref().unwrap().to_string())
79        .collect();
80    let field_idents: Vec<_> = fields.iter().map(|f| f.ident.as_ref().unwrap()).collect();
81
82    let gene = quote! {
83        impl #name {
84            pub fn columns() -> &'static [&'static str] {
85                &[#(#column_names),*]
86            }
87
88            pub fn values<'e>(&'e self) -> Vec<&'e (impl sqlx::Encode<'e, sqlx::Postgres> + sqlx::Type<sqlx::Postgres>)> {
89                vec![#(&self.#field_idents),*]
90            }
91        }
92    };
93
94    gene.into()
95}