1use darling::{FromDeriveInput, FromField, ast::Data, util::Ignored};
4use proc_macro::TokenStream;
5use proc_macro_error2::abort;
6use quote::ToTokens;
7use syn::{DeriveInput, Ident, Path, parse_macro_input};
8use thiserror::Error;
9
10mod delete;
11mod fetch;
12mod insert;
13mod stream;
14mod update;
15
16#[proc_macro_error2::proc_macro_error]
18#[proc_macro_derive(Entity, attributes(orm))]
19pub fn derive_response(input: TokenStream) -> TokenStream {
20 let args = parse_macro_input!(input as DeriveInput);
21
22 match generate(args) {
23 Ok(stream) => stream,
24 Err(err) => err.write_errors().into(),
25 }
26}
27
28fn generate(args: DeriveInput) -> Result<TokenStream, GeneratorError> {
29 let args: EntityArgs = EntityArgs::from_derive_input(&args)?;
30 let ident = args.ident.clone();
31
32 let args = match EntityCtx::try_from(args) {
33 Ok(v) => v,
34 Err(ParseCtxError::InvalidApplication) => {
35 abort!(ident, ParseCtxError::InvalidApplication)
36 }
37 };
38
39 let insert_stream = insert::generate_insert(&args);
40 let update_stream = update::generate_update(&args);
41 let stream_stream = stream::generate_stream(&args);
42 let delete_stream = delete::generate_delete(&args);
43 let get_by_id_stream = fetch::generate_fetch(&args);
44
45 let stream = quote::quote! {
46 #insert_stream
47 #update_stream
48 #stream_stream
49 #delete_stream
50 #get_by_id_stream
51 };
52
53 Ok(stream.into())
54}
55
56#[derive(Debug, Clone)]
57struct EntityCtx {
58 ident: Ident,
59 vis: syn::Visibility,
60 data: Vec<EntityFieldCtx>,
61 table: String,
62 soft_delete: Option<String>,
63}
64
65impl EntityCtx {
66 fn pks(&self) -> impl Iterator<Item = &EntityFieldCtx> {
67 self.data.iter().filter(|field| field.pk)
68 }
69
70 fn columns(&self) -> impl Iterator<Item = String> {
71 self.data.iter().cloned().map(|field| {
72 if let Some(cast) = field.cast {
73 format!(
74 r#"{ident} AS "{ident}!: {cast}""#,
75 ident = field.ident,
76 cast = cast.to_token_stream()
77 )
78 } else {
79 field.ident.to_string()
80 }
81 })
82 }
83}
84
85impl TryFrom<EntityArgs> for EntityCtx {
86 type Error = ParseCtxError;
87
88 fn try_from(value: EntityArgs) -> Result<Self, Self::Error> {
89 let mut data = vec![];
90
91 for row in value
92 .data
93 .take_struct()
94 .ok_or(ParseCtxError::InvalidApplication)?
95 {
96 data.push(row.try_into()?);
97 }
98
99 Ok(Self {
100 ident: value.ident,
101 vis: value.vis,
102 data,
103 table: value.table,
104 soft_delete: value.soft_delete,
105 })
106 }
107}
108
109#[derive(Debug, Clone)]
110struct EntityFieldCtx {
111 ident: Ident,
112 vis: syn::Visibility,
113 ty: syn::Type,
114 pk: bool,
115 generated: bool,
116 deref: bool,
117 as_ref: bool,
118 default: bool,
119 cast: Option<Path>,
120}
121
122impl EntityFieldCtx {
123 pub(crate) fn cast(&self) -> proc_macro2::TokenStream {
124 self.cast
125 .clone()
126 .map(|cast| {
127 quote::quote! {
128 as &#cast
129 }
130 })
131 .unwrap_or_default()
132 }
133}
134
135#[derive(Debug, Error)]
136enum ParseCtxError {
137 #[error("The `Entity` macro can only be applied to a struct with named fields")]
138 InvalidApplication,
139}
140
141impl TryFrom<EntityField> for EntityFieldCtx {
142 type Error = ParseCtxError;
143
144 fn try_from(value: EntityField) -> Result<Self, Self::Error> {
145 Ok(Self {
146 ident: value.ident.ok_or(ParseCtxError::InvalidApplication)?,
147 vis: value.vis,
148 ty: value.ty,
149 pk: value.pk,
150 generated: value.generated,
151 deref: value.deref,
152 as_ref: value.as_ref,
153 default: value.default,
154 cast: value.cast,
155 })
156 }
157}
158
159#[derive(Debug, FromDeriveInput)]
160#[darling(attributes(orm), forward_attrs(doc))]
161struct EntityArgs {
162 ident: Ident,
163 vis: syn::Visibility,
164 data: Data<Ignored, EntityField>,
165 table: String,
166 soft_delete: Option<String>,
167}
168
169#[derive(Debug, Clone, FromField)]
170#[darling(attributes(orm), forward_attrs(doc))]
171struct EntityField {
172 ident: Option<Ident>,
173 vis: syn::Visibility,
174 ty: syn::Type,
175 #[darling(default)]
176 pk: bool,
177 #[darling(default)]
178 generated: bool,
179 #[darling(default)]
180 default: bool,
181 #[darling(default)]
182 deref: bool,
183 #[darling(default)]
184 as_ref: bool,
185 cast: Option<syn::Path>,
186}
187
188#[derive(Debug, Error)]
189pub(crate) enum GeneratorError {
190 #[error("{0}")]
191 Syn(
192 #[source]
193 #[from]
194 syn::Error,
195 ),
196 #[error("{0}")]
197 Darling(
198 #[source]
199 #[from]
200 darling::Error,
201 ),
202}
203
204impl GeneratorError {
205 pub(crate) fn write_errors(self) -> proc_macro2::TokenStream {
206 match self {
207 Self::Syn(err) => err.to_compile_error(),
208 Self::Darling(err) => err.write_errors(),
209 }
210 }
211}