1extern crate proc_macro;
2mod uri;
3mod model;
4mod version;
5
6use proc_macro::TokenStream;
7
8use proc_macro2::{Ident, Span};
9use quote::quote;
10use syn::{parse_macro_input, Attribute, Data, DeriveInput, Fields};
11
12use uri::uribuilder;
13use crate::version::{
14 derive_version_parse_legacy,
15 derive_version_parse_root_uri,
16 derive_version_parse_data_uri
17};
18
19macro_rules! derive_input_boilerplate {
24 ($($field:ident),+ $(,)?; from $input:ident) => {
25 let temp_input = $input.clone();
26 let DeriveInput {
27 $(
28 $field,
29 )+
30 ..
31 } = parse_macro_input!(temp_input as DeriveInput);
32 };
33}
34
35macro_rules! empty_impl {
40 ($derive_name:path; from $input:ident) => {
41 {
42 derive_input_boilerplate!(ident, generics; from $input);
43 let where_clause = &generics.where_clause;
44 let crate_ident = get_crate_ident();
45 quote! {
46 impl #generics #crate_ident::$derive_name for #ident #generics #where_clause {}
47 }
48 }
49 };
50}
51
52macro_rules! new_ambiguous_ident {
54 ($e:expr, $($vars:expr),+ $(,)?) => {
55 Ident::new(
56 &format!($e, $($vars,)+),
57 Span::call_site()
58 )
59 };
60 ($e:expr) => {
61 Ident::new($e, Span::call_site())
62 }
63}
64
65type Attributes = Vec<Attribute>;
67
68fn get_crate_ident() -> Ident {
69 let pkg_name = std::env::var("CARGO_PKG_NAME").unwrap_or("crate".into());
70 new_ambiguous_ident!(if pkg_name == "oxinat_core" {
71 "crate"
72 } else {
73 "oxinat_core"
74 })
75}
76
77#[proc_macro_derive(FullUri)]
78pub fn derive_alluri(input: TokenStream) -> TokenStream {
79 let mut gen = TokenStream::new();
80 [
81 derive_adminuri,
82 derive_archiveuri,
83 derive_authuri,
84 derive_dicomuri,
85 derive_eventuri,
86 derive_experimenturi,
87 derive_pluginuri,
88 derive_projectsuri,
89 derive_serviceuri,
90 derive_subjecturi,
91 derive_sysuri,
92 derive_usersuri
93 ].iter().for_each(|deriver| gen.extend(deriver(input.clone())));
94 gen
95}
96
97#[proc_macro_derive(AdminUri)]
102pub fn derive_adminuri(input: TokenStream) -> TokenStream {
103 derive_input_boilerplate!(attrs; from input);
104
105 let mut gen = quote! {};
107 if !derive_version_parse_legacy(&attrs) {
108 gen.extend(empty_impl!(AdminUri; from input));
109 }
110 gen.extend(empty_impl!(AdminUriLegacy; from input));
111 gen.into()
112}
113
114#[proc_macro_derive(ArchiveUri)]
118pub fn derive_archiveuri(input: TokenStream) -> TokenStream {
119 empty_impl!(ArchiveUri; from input).into()
120}
121
122#[proc_macro_derive(AuthUri)]
126pub fn derive_authuri(input: TokenStream) -> TokenStream {
127 empty_impl!(AuthUriLegacy; from input).into()
128}
129
130#[proc_macro_derive(DicomUri)]
134pub fn derive_dicomuri(input: TokenStream) -> TokenStream {
135 empty_impl!(DicomUri; from input).into()
136}
137
138#[proc_macro_derive(EventUri)]
142pub fn derive_eventuri(input: TokenStream) -> TokenStream {
143 empty_impl!(EventsUri; from input).into()
144}
145
146#[proc_macro_derive(ExperimentUri)]
150pub fn derive_experimenturi(input: TokenStream) -> TokenStream {
151 let mut gen = quote! {};
152 gen.extend(empty_impl!(ExperimentUri; from input));
153 gen.extend(empty_impl!(ExperimentUriArchive; from input));
154 gen.into()
155}
156
157#[proc_macro_derive(PluginUri)]
161pub fn derive_pluginuri(input: TokenStream) -> TokenStream {
162 empty_impl!(PluginUri; from input).into()
163}
164
165#[proc_macro_derive(ProjectUri)]
170pub fn derive_projectsuri(input: TokenStream) -> TokenStream {
171 derive_input_boilerplate!(attrs; from input);
172 let mut gen = quote! {};
173 if !derive_version_parse_legacy(&attrs) {
174 gen.extend(empty_impl!(ProjectUri; from input))
175 }
176 gen.extend(empty_impl!(ProjectUriArchive; from input));
177 gen.extend(empty_impl!(ProjectUriLegacy; from input));
178 gen.into()
179}
180
181#[proc_macro_derive(ServicesUri)]
186pub fn derive_serviceuri(input: TokenStream) -> TokenStream {
187 empty_impl!(ServicesUriLegacy; from input).into()
188}
189
190#[proc_macro_derive(SubjectUri)]
195pub fn derive_subjecturi(input: TokenStream) -> TokenStream {
196 let mut gen = quote! {};
197 gen.extend(empty_impl!(SubjectUriLegacy; from input));
198 gen.extend(empty_impl!(SubjectUriArchive; from input));
199 gen.into()
200}
201
202#[proc_macro_derive(SystemUri)]
207pub fn derive_sysuri(input: TokenStream) -> TokenStream {
208 empty_impl!(SystemUri; from input).into()
209}
210
211#[proc_macro_derive(UsersUri)]
216pub fn derive_usersuri(input: TokenStream) -> TokenStream {
217 derive_input_boilerplate!(attrs; from input);
218
219 let mut gen = quote! {};
220 if !derive_version_parse_legacy(&attrs) {
221 gen.extend(empty_impl!(UsersUri; from input));
222 }
223 gen.extend(empty_impl!(UsersUriLegacy; from input));
224 gen.into()
225}
226
227#[proc_macro_derive(ModelField)]
234pub fn derive_model_field(input: TokenStream) -> TokenStream {
235 model::build_property(input)
236}
237
238#[proc_macro]
265pub fn uri_builder_alias(input: TokenStream) -> TokenStream {
266 let ident = parse_macro_input!(input as Ident);
267 let trait_doc = "This is an alias trait for traits common in subsequent implmentations.";
268 let impl_doc = &format!("Generate implementations of `{ident}`.");
269 let impl_name = Ident::new(&format!("Impl{ident}"), Span::call_site());
270 quote! {
271 #[doc=#trait_doc]
272 pub trait #ident: UriBuilder + Clone + Debug {}
273 #[doc=#impl_doc]
274 macro_rules! #impl_name {
275 ($(($kind:ty)),+ $(,)?) => {
276 $(impl #ident for $kind {})+
277 };
278 ($(($kind:ty, $parent:ident)),+ $(,)?) => {
279 $(
280 impl<$parent> #ident for $kind
281 where
282 $parent: #ident,
283 {}
284 )+
285 };
286 }
287 }.into()
288}
289
290#[proc_macro_derive(UriBuilder, attributes(parent, match_path, param, validator))]
297pub fn derive_uribuilder(input: TokenStream) -> TokenStream {
298 uribuilder::build(input)
299}
300
301#[proc_macro_derive(Version, attributes(version))]
305pub fn derive_version(input: TokenStream) -> TokenStream {
306 derive_input_boilerplate!(attrs, data, generics, ident; from input);
311 let where_clause = &generics.where_clause;
312 let crate_ident = get_crate_ident();
313
314 let root_uri = derive_version_parse_root_uri(&attrs)
318 .unwrap_or_else(|_| ident.to_string().to_lowercase());
319 let data_uri = derive_version_parse_data_uri(&attrs).unwrap();
320
321 let mut gen = quote! {};
322 gen.extend(quote! {
323 impl #generics #crate_ident::Version for #ident #generics #where_clause {
324 fn root_uri(&self) -> String {
325 String::from(#root_uri)
326 }
327
328 fn data_uri(&self) -> String {
329 String::from(#data_uri)
330 }
331 }
332 });
333 gen.extend(quote! {
334 impl #generics #crate_ident::UriBuilder for #ident #generics #where_clause {
335 fn build(&self) -> oxinat_core::BuildResult {
336 Ok(self.root_uri())
337 }
338 }
339 });
340 gen.extend(quote! {
341 impl #generics std::fmt::Display for #ident #generics #where_clause {
342 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
343 write!(f, "{}", self.root_uri())
344 }
345 }
346 });
347
348 let mut default_calls = quote! {};
349 match data {
350 Data::Struct(d) => d.fields.to_owned(),
351 _ => Fields::Unit,
352 }
353 .iter()
354 .for_each(|f| {
355 let field_ident = &f.ident;
356 let field_type = &f.ty;
357 default_calls.extend(quote! {
358 #field_ident: #field_type::default(),
359 })
360 });
361 gen.extend(quote! {
362 impl #generics Default for #ident #generics #where_clause {
363 fn default() -> Self {
364 Self { #default_calls }
365 }
366 }
367 });
368 gen.into()
369}