utility_macros_internals/derive/
required.rs1use 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}