1#![forbid(unsafe_code, future_incompatible)]
2#![deny(
3 missing_debug_implementations,
4 nonstandard_style,
5 missing_copy_implementations,
6 unused_qualifications,
7 missing_docs,
8 rustdoc::missing_crate_level_docs
9)]
10#![warn(clippy::pedantic)]
11#![doc = include_str!("../docs.md")]
12
13use proc_macro::TokenStream;
14use proc_macro2::TokenStream as TokenStream2;
15use quote::quote;
16
17mod common_settings;
18mod copy_detection;
19mod deref_handling;
20mod errors;
21mod field;
22mod field_attributes;
23mod field_method_attributes;
24mod method;
25mod option_handling;
26mod query;
27mod resolved;
28mod r#struct;
29mod struct_attributes;
30mod struct_method_attributes;
31
32#[cfg(test)]
33mod coverage_tests;
34
35pub(crate) use common_settings::{CommonSettings, with_common_settings};
36pub(crate) use field::Field;
37pub(crate) use field_attributes::FieldAttributes;
38pub(crate) use field_method_attributes::FieldMethodAttributes;
39pub(crate) use method::{Method, MethodSettings, with_methods};
40pub(crate) use query::Query;
41pub(crate) use resolved::Resolved;
42pub(crate) use r#struct::Struct;
43pub(crate) use struct_attributes::StructAttributes;
44pub(crate) use struct_method_attributes::StructMethodAttributes;
45use syn::Attribute;
46
47#[proc_macro_derive(Fieldwork, attributes(fieldwork, field))]
49pub fn derive_fieldwork(input: TokenStream) -> TokenStream {
50 derive_fieldwork_internal(input.into()).into()
51}
52fn derive_fieldwork_internal(input: TokenStream2) -> TokenStream2 {
53 let Struct {
54 ident,
55 fields,
56 attributes,
57 generics,
58 } = match syn::parse2(input) {
59 Ok(ok) => ok,
60 Err(e) => return e.to_compile_error(),
61 };
62
63 let impls = fields
64 .iter()
65 .flat_map(|field| {
66 Method::all()
67 .iter()
68 .filter_map(|method| Query::new(method, field, &attributes).resolve())
69 })
70 .map(|resolved| resolved.build())
71 .collect::<TokenStream2>();
72
73 let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
74 quote! {
75 impl #impl_generics #ident #type_generics #where_clause {
76 #impls
77 }
78 }
79}
80
81pub(crate) fn is_fieldwork_attr(attr: &Attribute) -> bool {
82 let path = attr.path();
83 path.is_ident("fieldwork") || path.is_ident("field")
84}