utility_macros_internals/derive/
partial.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_option, is_option},
11};
12
13pub fn partial_impl(
14 DeriveInput {
15 attrs,
16 ident: type_ident,
17 data,
18 ..
19 }: DeriveInput,
20) -> TokenStream {
21 let ContainerAttributesData {
22 ident: partial_ident,
23 derives,
24 rename_all,
25 } = container_attributes("partial", attrs, format_ident!("Partial{}", type_ident));
26
27 let field_attr_context = FieldAttributesContext {
28 helper: "partial",
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_partial_body = Vec::new();
39 let mut to_full_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: type_ident,
48 ..
49 } = field;
50
51 let type_ident = type_ident.clone().expect("Expected ident");
52
53 let FieldAttributesData {
54 ident: partial_ident,
55 skip,
56 } = field_attributes(&field_attr_context, field);
57
58 if skip {
59 if is_option(ty) {
60 to_full_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_full_body.push(quote! {
71 #type_ident: Default::default(),
72 });
73 impl_partial_eq = false;
74 }
75 continue;
76 }
77
78 let opt_ty = as_option(ty);
79 struct_body.push(quote! {
80 #vis #partial_ident: #opt_ty,
81 });
82
83 static_assertions.push(quote! {
84 ::utility_macros::_um::_sa::assert_impl_all!(#opt_ty: Clone);
85 });
86
87 if is_option(ty) {
88 to_partial_body.push(quote! {
89 #partial_ident: self.#type_ident.clone(),
90 });
91 to_full_body.push(quote! {
92 #type_ident: self.#partial_ident.clone(),
93 });
94 partial_eq.push(quote! {
95 self.#type_ident == other.#partial_ident
96 });
97 } else {
98 to_partial_body.push(quote! {
99 #partial_ident: Some(self.#type_ident.clone()),
100 });
101 to_full_body.push(quote! {
102 #type_ident: self.#partial_ident.clone().ok_or_else(|| ::utility_macros::_um::error::Error::MissingField(stringify!(#partial_ident)))?,
103 });
104 partial_eq.push(quote! {
105 other.#partial_ident.clone().map_or(false, |val| self.#type_ident == val)
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<#partial_ident> for #type_ident {
121 fn eq(&self, other: &#partial_ident) -> bool {
122 #(#partial_eq)&& *
123 }
124 }
125 }
126 } else {
127 quote! {
128 impl PartialEq<#partial_ident> for #type_ident {
129 fn eq(&self, _: &#partial_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 #partial_ident {
141 #(#struct_body)*
142 }
143
144 impl ::utility_macros::_um::partial::HasPartial for #type_ident {
145 type Partial = #partial_ident;
146
147 fn partial(&self) -> Self::Partial {
148 Self::Partial {
149 #(#to_partial_body)*
150 }
151 }
152 }
153
154 impl ::utility_macros::_um::partial::Partial for #partial_ident {
155 type Type = #type_ident;
156
157 fn type_(&self) -> ::utility_macros::_um::error::Result<Self::Type> {
158 Ok(#type_ident {
159 #(#to_full_body)*
160 })
161 }
162 }
163
164 impl From<#type_ident> for #partial_ident {
165 fn from(type_: #type_ident) -> Self {
166 ::utility_macros::_um::partial::HasPartial::partial(&type_)
167 }
168 }
169
170 impl TryFrom<#partial_ident> for #type_ident {
171 type Error = ::utility_macros::_um::error::Error;
172
173 fn try_from(partial: #partial_ident) -> ::utility_macros::_um::error::Result<Self> {
174 ::utility_macros::_um::partial::Partial::type_(&partial)
175 }
176 }
177 #partial_eq_impl
178 }
179}