utility_macros_internals/derive/
required.rs

1use proc_macro2::TokenStream;
2use quote::{format_ident, quote};
3use syn::{Data, DeriveInput, Field};
4
5use crate::{
6    derive::{
7        container_attributes::{container_attributes, ContainerAttributesData},
8        field_attributes::{field_attributes, FieldAttributesContext, FieldAttributesData},
9    },
10    option::{as_required, is_option},
11};
12
13pub fn required_impl(
14    DeriveInput {
15        attrs,
16        ident: type_ident,
17        data,
18        ..
19    }: DeriveInput,
20) -> TokenStream {
21    let ContainerAttributesData {
22        ident: required_ident,
23        derives,
24        rename_all,
25    } = container_attributes("required", attrs, format_ident!("Required{}", type_ident));
26
27    let field_attr_context = FieldAttributesContext {
28        helper: "required",
29        rename_all,
30    };
31
32    let Data::Struct(data) = data else {
33        panic!("Expected struct")
34    };
35
36    let mut static_assertions = Vec::new();
37    let mut struct_body = Vec::new();
38    let mut to_required_body = Vec::new();
39    let mut to_type_body = Vec::new();
40    let mut impl_partial_eq = true;
41    let mut partial_eq = Vec::new();
42
43    for field in &data.fields {
44        let Field {
45            vis,
46            ty,
47            ident: full_ident,
48            ..
49        } = field;
50
51        let type_ident = full_ident.clone().expect("Expected ident");
52
53        let FieldAttributesData {
54            ident: required_ident,
55            skip,
56        } = field_attributes(&field_attr_context, field);
57
58        if skip {
59            if is_option(ty) {
60                to_type_body.push(quote! {
61                    #type_ident: None,
62                });
63                partial_eq.push(quote! {
64                    self.#type_ident.is_none()
65                });
66            } else {
67                static_assertions.push(quote! {
68                    ::utility_macros::_um::_sa::assert_impl_all!(#ty: Default);
69                });
70                to_type_body.push(quote! {
71                    #type_ident: Default::default(),
72                });
73                impl_partial_eq = false;
74            }
75            continue;
76        }
77
78        let required_ty = as_required(ty);
79        struct_body.push(quote! {
80            #vis #required_ident: #required_ty,
81        });
82
83        static_assertions.push(quote! {
84            ::utility_macros::_um::_sa::assert_impl_all!(#required_ty: Clone);
85        });
86
87        if is_option(ty) {
88            to_required_body.push(quote! {
89                #required_ident: self.#type_ident.clone().ok_or_else(|| ::utility_macros::_um::error::Error::MissingField(stringify!(#required_ident)))?,
90            });
91            to_type_body.push(quote! {
92                #type_ident: Some(self.#required_ident.clone()),
93            });
94            partial_eq.push(quote! {
95                self.#type_ident.clone().map_or(false, |val| val == other.#required_ident)
96            });
97        } else {
98            to_required_body.push(quote! {
99                #required_ident: self.#type_ident.clone(),
100            });
101            to_type_body.push(quote! {
102                #type_ident: self.#required_ident.clone(),
103            });
104            partial_eq.push(quote! {
105                self.#type_ident == other.#required_ident
106            });
107        }
108    }
109
110    let derives = if derives.is_empty() {
111        quote! {}
112    } else {
113        quote! {
114            #[derive(#(#derives),*)]
115        }
116    };
117
118    let partial_eq_impl = if impl_partial_eq {
119        quote! {
120            impl PartialEq<#required_ident> for #type_ident {
121                fn eq(&self, other: &#required_ident) -> bool {
122                    #(#partial_eq)&& *
123                }
124            }
125        }
126    } else {
127        quote! {
128            impl PartialEq<#required_ident> for #type_ident {
129                fn eq(&self, _: &#required_ident) -> bool {
130                    panic!("Partial equality can't be implemented for types that have skipped, non-optional fields");
131                }
132            }
133        }
134    };
135
136    quote! {
137        #(#static_assertions)*
138
139        #derives
140        pub struct #required_ident {
141            #(#struct_body)*
142        }
143
144        impl ::utility_macros::_um::required::HasRequired for #type_ident {
145            type Required = #required_ident;
146
147            fn required(&self) -> ::utility_macros::_um::error::Result<Self::Required> {
148                Ok(Self::Required {
149                    #(#to_required_body)*
150                })
151            }
152        }
153
154        impl ::utility_macros::_um::required::Required for #required_ident {
155            type Type = #type_ident;
156
157            fn type_(&self) -> Self::Type {
158                #type_ident {
159                    #(#to_type_body)*
160                }
161            }
162        }
163
164        impl TryFrom<#type_ident> for #required_ident {
165            type Error = ::utility_macros::_um::error::Error;
166
167            fn try_from(value: #type_ident) -> ::utility_macros::_um::error::Result<Self> {
168                ::utility_macros::_um::required::HasRequired::required(&value)
169            }
170        }
171
172        impl From<#required_ident> for #type_ident {
173            fn from(required: #required_ident) -> Self {
174                ::utility_macros::_um::required::Required::type_(&required)
175            }
176        }
177
178       #partial_eq_impl
179    }
180}