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 default: bool,
118 cast: Option<Path>,
119}
120
121impl EntityFieldCtx {
122 pub(crate) fn cast(&self) -> proc_macro2::TokenStream {
123 self.cast
124 .clone()
125 .map(|cast| {
126 quote::quote! {
127 as &#cast
128 }
129 })
130 .unwrap_or_default()
131 }
132}
133
134#[derive(Debug, Error)]
135enum ParseCtxError {
136 #[error("The `Entity` macro can only be applied to a struct with named fields")]
137 InvalidApplication,
138}
139
140impl TryFrom<EntityField> for EntityFieldCtx {
141 type Error = ParseCtxError;
142
143 fn try_from(value: EntityField) -> Result<Self, Self::Error> {
144 Ok(Self {
145 ident: value.ident.ok_or(ParseCtxError::InvalidApplication)?,
146 vis: value.vis,
147 ty: value.ty,
148 pk: value.pk,
149 generated: value.generated,
150 deref: value.deref,
151 default: value.default,
152 cast: value.cast,
153 })
154 }
155}
156
157#[derive(Debug, FromDeriveInput)]
158#[darling(attributes(orm), forward_attrs(doc))]
159struct EntityArgs {
160 ident: Ident,
161 vis: syn::Visibility,
162 data: Data<Ignored, EntityField>,
163 table: String,
164 soft_delete: Option<String>,
165}
166
167#[derive(Debug, Clone, FromField)]
168#[darling(attributes(orm), forward_attrs(doc))]
169struct EntityField {
170 ident: Option<Ident>,
171 vis: syn::Visibility,
172 ty: syn::Type,
173 #[darling(default)]
174 pk: bool,
175 #[darling(default)]
176 generated: bool,
177 #[darling(default)]
178 default: bool,
179 #[darling(default)]
180 deref: bool,
181 cast: Option<syn::Path>,
182}
183
184#[derive(Debug, Error)]
185pub(crate) enum GeneratorError {
186 #[error("{0}")]
187 Syn(
188 #[source]
189 #[from]
190 syn::Error,
191 ),
192 #[error("{0}")]
193 Darling(
194 #[source]
195 #[from]
196 darling::Error,
197 ),
198}
199
200impl GeneratorError {
201 pub(crate) fn write_errors(self) -> proc_macro2::TokenStream {
202 match self {
203 Self::Syn(err) => err.to_compile_error(),
204 Self::Darling(err) => err.write_errors(),
205 }
206 }
207}