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