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}
63
64impl EntityCtx {
65 fn pks(&self) -> impl Iterator<Item = &EntityFieldCtx> {
66 self.data.iter().filter(|field| field.pk)
67 }
68
69 fn columns(&self) -> impl Iterator<Item = String> {
70 self.data.iter().cloned().map(|field| {
71 if let Some(cast) = field.cast {
72 format!(
73 r#"{ident} AS "{ident}!: {cast}""#,
74 ident = field.ident,
75 cast = cast.to_token_stream()
76 )
77 } else {
78 field.ident.to_string()
79 }
80 })
81 }
82}
83
84impl TryFrom<EntityArgs> for EntityCtx {
85 type Error = ParseCtxError;
86
87 fn try_from(value: EntityArgs) -> Result<Self, Self::Error> {
88 let mut data = vec![];
89
90 for row in value
91 .data
92 .take_struct()
93 .ok_or(ParseCtxError::InvalidApplication)?
94 {
95 data.push(row.try_into()?);
96 }
97
98 Ok(Self {
99 ident: value.ident,
100 vis: value.vis,
101 data,
102 table: value.table,
103 })
104 }
105}
106
107#[derive(Debug, Clone)]
108struct EntityFieldCtx {
109 ident: Ident,
110 vis: syn::Visibility,
111 ty: syn::Type,
112 pk: bool,
113 generated: bool,
114 deref: bool,
115 default: bool,
116 cast: Option<Path>,
117}
118
119impl EntityFieldCtx {
120 pub(crate) fn cast(&self) -> proc_macro2::TokenStream {
121 self.cast
122 .clone()
123 .map(|cast| {
124 quote::quote! {
125 as &#cast
126 }
127 })
128 .unwrap_or_default()
129 }
130}
131
132#[derive(Debug, Error)]
133enum ParseCtxError {
134 #[error("The `Entity` macro can only be applied to a struct with named fields")]
135 InvalidApplication,
136}
137
138impl TryFrom<EntityField> for EntityFieldCtx {
139 type Error = ParseCtxError;
140
141 fn try_from(value: EntityField) -> Result<Self, Self::Error> {
142 Ok(Self {
143 ident: value.ident.ok_or(ParseCtxError::InvalidApplication)?,
144 vis: value.vis,
145 ty: value.ty,
146 pk: value.pk,
147 generated: value.generated,
148 deref: value.deref,
149 default: value.default,
150 cast: value.cast,
151 })
152 }
153}
154
155#[derive(Debug, FromDeriveInput)]
156#[darling(attributes(orm), forward_attrs(doc))]
157struct EntityArgs {
158 ident: Ident,
159 vis: syn::Visibility,
160 data: Data<Ignored, EntityField>,
161 table: String,
162}
163
164#[derive(Debug, Clone, FromField)]
165#[darling(attributes(orm), forward_attrs(doc))]
166struct EntityField {
167 ident: Option<Ident>,
168 vis: syn::Visibility,
169 ty: syn::Type,
170 #[darling(default)]
171 pk: bool,
172 #[darling(default)]
173 generated: bool,
174 #[darling(default)]
175 default: bool,
176 #[darling(default)]
177 deref: bool,
178 cast: Option<syn::Path>,
179}
180
181#[derive(Debug, Error)]
182pub(crate) enum GeneratorError {
183 #[error("{0}")]
184 Syn(
185 #[source]
186 #[from]
187 syn::Error,
188 ),
189 #[error("{0}")]
190 Darling(
191 #[source]
192 #[from]
193 darling::Error,
194 ),
195}
196
197impl GeneratorError {
198 pub(crate) fn write_errors(self) -> proc_macro2::TokenStream {
199 match self {
200 Self::Syn(err) => err.to_compile_error(),
201 Self::Darling(err) => err.write_errors(),
202 }
203 }
204}