xops_core/binop/write.rs
1use proc_macro2::TokenStream;
2use quote::{quote, ToTokens, TokenStreamExt};
3use syn::parse_quote;
4
5use crate::{utils::TypeConversion, BinOpFn, BinOpImpl, BinOpOutput};
6
7impl BinOpImpl {
8 /// If `lhs_ty = &A`, this returns an implementation of `A op B` utilizing `&A op B`.
9 ///
10 /// If `lhs_ty` is not a reference type, this returns `None`.
11 ///
12 /// In other words, if `self` is of the form
13 /// ```
14 /// impl Op<B> for &A {
15 /// ...
16 /// }
17 /// ```
18 /// then this produces the implementation
19 /// ```
20 /// impl Op<B> for A {
21 /// ...
22 /// fn op(self, rhs: B) -> Self::Output {
23 /// (&self).op(rhs)
24 /// }
25 /// }
26 /// ```
27 pub fn try_deref_lhs(&self) -> Option<Self> {
28 let lhs_ty = self.lhs_ty.as_deref()?;
29 let rhs_ty = &self.rhs_ty;
30 let fn_ident = &self.item_fn.ident;
31 let item_fn = parse_quote! {
32 fn #fn_ident(self, rhs: #rhs_ty) -> Self::Output {
33 (&self).#fn_ident(rhs)
34 }
35 };
36
37 Some(BinOpImpl {
38 lhs_ty,
39 item_fn,
40 ..self.clone()
41 })
42 }
43
44 /// If `rhs_ty = &B`, this returns an implementation of `A op B` utilizing `A op &B`.
45 ///
46 /// If `rhs_ty` is not a reference type, this returns `None`.
47 ///
48 /// In other words, if `self` is of the form
49 /// ```
50 /// impl Op<&B> for A {
51 /// ...
52 /// }
53 /// ```
54 /// then this produces the implementation
55 /// ```
56 /// impl Op<B> for A {
57 /// ...
58 /// fn op(self, rhs: B) -> Self::Output {
59 /// self.op(&rhs)
60 /// }
61 /// }
62 /// ```
63 pub fn try_deref_rhs(&self) -> Option<Self> {
64 let rhs_ty = self.rhs_ty.as_deref()?;
65 let fn_ident = &self.item_fn.ident;
66 let item_fn = parse_quote! {
67 fn #fn_ident(self, rhs: #rhs_ty) -> Self::Output {
68 self.#fn_ident(&rhs)
69 }
70 };
71
72 Some(BinOpImpl {
73 rhs_ty,
74 item_fn,
75 ..self.clone()
76 })
77 }
78
79 /// If `lhs_ty = &A` and `rhs_ty = &B`, this returns an implementation of `A op B` utilizing `&A op &B`.
80 ///
81 /// If `lhs_ty` and `rhs_ty` not a references type, this returns `None`.
82 ///
83 /// In other words, if `self` is of the form
84 /// ```
85 /// impl Op<&B> for &A {
86 /// ...
87 /// }
88 /// ```
89 /// then this produces the implementation
90 /// ```
91 /// impl Op<B> for A {
92 /// ...
93 /// fn op(self, rhs: B) -> Self::Output {
94 /// (&self).op(&rhs)
95 /// }
96 /// }
97 /// ```
98 pub fn try_deref_both(&self) -> Option<Self> {
99 let lhs_ty = self.lhs_ty.as_deref()?;
100 let rhs_ty = self.rhs_ty.as_deref()?;
101 let fn_ident = &self.item_fn.ident;
102 let item_fn = parse_quote! {
103 fn #fn_ident(self, rhs: #rhs_ty) -> Self::Output {
104 (&self).#fn_ident(&rhs)
105 }
106 };
107
108 Some(BinOpImpl {
109 lhs_ty,
110 rhs_ty,
111 item_fn,
112 ..self.clone()
113 })
114 }
115
116 /// Returns an implementation of `&A op B` utilizing `A op B`.
117 ///
118 /// The macro user must enure that `A: Clone`.
119 ///
120 /// In other words, if `self` is of the form
121 /// ```
122 /// impl Op<B> for A {
123 /// ...
124 /// }
125 /// ```
126 /// then this produces the implementation
127 /// ```
128 /// impl Op<B> for &A {
129 /// ...
130 /// fn op(self, rhs: B) -> Self::Output {
131 /// self.clone().op(rhs)
132 /// }
133 /// }
134 /// ```
135 pub fn ref_lhs_clone(&self) -> Self {
136 let fn_ident = &self.item_fn.ident;
137 let lhs_ty = self.lhs_ty.as_ref();
138 let rhs_ty = &self.rhs_ty;
139
140 let item_fn = parse_quote! {
141 fn #fn_ident(self, rhs: #rhs_ty) -> Self::Output {
142 self.clone().#fn_ident(rhs)
143 }
144 };
145
146 BinOpImpl {
147 lhs_ty,
148 item_fn,
149 ..self.clone()
150 }
151 }
152
153 /// Returns an implementation of `A op &B` utilizing `A op B`.
154 ///
155 /// The macro user must enure that `B: Clone`.
156 ///
157 /// In other words, if `self` is of the form
158 /// ```
159 /// impl Op<B> for A {
160 /// ...
161 /// }
162 /// ```
163 /// then this produces the implementation
164 /// ```
165 /// impl Op<&B> for A {
166 /// ...
167 /// fn op(self, rhs: B) -> Self::Output {
168 /// self.op(rhs.clone())
169 /// }
170 /// }
171 /// ```
172 pub fn ref_rhs_clone(&self) -> Self {
173 let fn_ident = &self.item_fn.ident;
174 let rhs_ty = self.rhs_ty.as_ref();
175
176 let item_fn = parse_quote! {
177 fn #fn_ident(self, rhs: #rhs_ty) -> Self::Output {
178 self.#fn_ident(rhs.clone())
179 }
180 };
181
182 BinOpImpl {
183 rhs_ty,
184 item_fn,
185 ..self.clone()
186 }
187 }
188
189 /// Returns an implementation of `&A op &B` utilizing `A op B`.
190 ///
191 /// The macro user must enure that `A: Clone` and `B: Clone`.
192 ///
193 /// In other words, if `self` is of the form
194 /// ```
195 /// impl Op<B> for A {
196 /// ...
197 /// }
198 /// ```
199 /// then this produces the implementation
200 /// ```
201 /// impl Op<&B> for &A {
202 /// ...
203 /// fn op(self, rhs: B) -> Self::Output {
204 /// self.clone().op(rhs.clone())
205 /// }
206 /// }
207 /// ```
208 pub fn ref_both_clone(&self) -> Self {
209 let fn_ident = &self.item_fn.ident;
210 let lhs_ty = self.lhs_ty.as_ref();
211 let rhs_ty = self.rhs_ty.as_ref();
212
213 let item_fn = parse_quote! {
214 fn #fn_ident(self, rhs: #rhs_ty) -> Self::Output {
215 self.clone().#fn_ident(rhs.clone())
216 }
217 };
218
219 BinOpImpl {
220 lhs_ty,
221 rhs_ty,
222 item_fn,
223 ..self.clone()
224 }
225 }
226
227 /// Returns an implementation of `B op A` utilizing `A op B`.
228 ///
229 /// The macro user must enure that some `impl Op<A> for B` does not exist elsewhere.
230 ///
231 /// In other words, if `self` is of the form
232 /// ```
233 /// impl Op<B> for A {
234 /// ...
235 /// }
236 /// ```
237 /// then this produces the implementation
238 /// ```
239 /// impl Op<A> for B {
240 /// ...
241 /// fn op(self, rhs: A) -> Self::Output {
242 /// rhs.op(self)
243 /// }
244 /// }
245 /// ```
246 pub fn commute(&self) -> Self {
247 let lhs_ty = self.rhs_ty.clone();
248 let rhs_ty = self.lhs_ty.clone();
249
250 let fn_ident = &self.item_fn.ident;
251 let item_fn = parse_quote! {
252 fn #fn_ident(self, rhs: #rhs_ty) -> Self::Output {
253 rhs.#fn_ident(self)
254 }
255 };
256
257 BinOpImpl {
258 lhs_ty,
259 rhs_ty,
260 item_fn,
261 ..self.clone()
262 }
263 }
264}
265
266// impl ToTokens -----------------------------------------------------------------------------------
267
268impl ToTokens for BinOpOutput {
269 fn to_tokens(&self, tokens: &mut TokenStream) {
270 self.type_token.to_tokens(tokens);
271 self.ident.to_tokens(tokens);
272 self.eq_token.to_tokens(tokens);
273 self.ty.to_tokens(tokens);
274 self.semi_token.to_tokens(tokens);
275 }
276}
277
278impl ToTokens for BinOpFn {
279 fn to_tokens(&self, tokens: &mut TokenStream) {
280 tokens.append_all(&self.attrs);
281 self.fn_token.to_tokens(tokens);
282 self.ident.to_tokens(tokens);
283 self.paren_token.surround(tokens, |tokens| {
284 self.lhs_arg.to_tokens(tokens);
285 self.comma_token.to_tokens(tokens);
286 self.rhs_arg.to_tokens(tokens);
287 });
288 self.arrow_token.to_tokens(tokens);
289 self.out_ty.to_tokens(tokens);
290 self.block.to_tokens(tokens);
291 }
292}
293
294macro_rules! lr_angled {
295 ($item:expr) => {{
296 let rhs_ty = $item;
297 quote!(<#rhs_ty>)
298 }};
299}
300
301impl ToTokens for BinOpImpl {
302 fn to_tokens(&self, tokens: &mut TokenStream) {
303 tokens.append_all(&self.attrs);
304 self.impl_token.to_tokens(tokens);
305 self.generics.to_tokens(tokens);
306 self.trait_.to_tokens(tokens);
307 tokens.append_all(lr_angled!(&self.rhs_ty));
308 self.for_token.to_tokens(tokens);
309 self.lhs_ty.to_tokens(tokens);
310 self.generics.where_clause.to_tokens(tokens);
311 self.brace_token.surround(tokens, |tokens| {
312 self.item_out.to_tokens(tokens);
313 self.item_fn.to_tokens(tokens);
314 });
315 }
316}