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}