checked_rs_macro_impl/
common_impl.rs

1use proc_macro2::TokenStream;
2use quote::{format_ident, quote};
3
4use crate::params::{BehaviorArg, NumberArg, NumberKind, Params};
5
6pub fn define_guard(name: &syn::Ident, guard_name: &syn::Ident, params: &Params) -> TokenStream {
7    let integer = params.integer;
8
9    quote! {
10        pub struct #guard_name<'a>(#integer, &'a mut #name);
11
12        impl<'a> std::ops::Deref for #guard_name<'a> {
13            type Target = #integer;
14
15            #[inline(always)]
16            fn deref(&self) -> &Self::Target {
17                &self.0
18            }
19        }
20
21        impl<'a> std::ops::DerefMut for #guard_name<'a> {
22            #[inline(always)]
23            fn deref_mut(&mut self) -> &mut Self::Target {
24                &mut self.0
25            }
26        }
27
28        impl<'a> AsRef<#integer> for #guard_name<'a> {
29            #[inline(always)]
30            fn as_ref(&self) -> &#integer {
31                &self.0
32            }
33        }
34
35        impl<'a> AsMut<#integer> for #guard_name<'a> {
36            #[inline(always)]
37            fn as_mut(&mut self) -> &mut #integer {
38                &mut self.0
39            }
40        }
41
42        impl<'a> Drop for #guard_name<'a> {
43            fn drop(&mut self) {
44                #[cfg(debug_assertions)]
45                {
46                    eprintln!("A `Guard` was dropped without calling `commit` or `discard` first");
47                }
48            }
49        }
50
51        impl<'a> #guard_name<'a> {
52            #[inline(always)]
53            pub(self) fn new(val: &'a mut #name) -> Self {
54                Self(val.into_primitive(), val)
55            }
56
57            #[inline(always)]
58            pub fn is_changed(&self) -> bool {
59                let a = self.0;
60                let b = self.1.into_primitive();
61
62                a != b
63            }
64
65            #[inline(always)]
66            pub fn check(&self) -> ::anyhow::Result<()> {
67                #name::validate(self.0)?;
68                Ok(())
69            }
70
71            #[inline(always)]
72            pub fn commit(self) -> ::anyhow::Result<(), Self> {
73                let mut this = std::mem::ManuallyDrop::new(self);
74
75                match this.check() {
76                    ::anyhow::Result::Ok(_) => {
77                        *this.1 = <#name as ClampedInteger<#integer>>::from_primitive(this.0).expect("value should be within bounds");
78                        ::anyhow::Result::Ok(())
79                    }
80                    ::anyhow::Result::Err(_) => ::anyhow::Result::Err(std::mem::ManuallyDrop::into_inner(this)),
81                }
82            }
83
84            #[inline(always)]
85            pub fn discard(self) {
86                std::mem::forget(self);
87            }
88        }
89    }
90}
91
92pub fn impl_deref(name: &syn::Ident, params: &Params) -> TokenStream {
93    let integer = params.integer;
94
95    quote! {
96        impl std::ops::Deref for #name {
97            type Target = #integer;
98
99            #[inline(always)]
100            fn deref(&self) -> &Self::Target {
101                self.as_primitive()
102            }
103        }
104
105        impl AsRef<#integer> for #name {
106            #[inline(always)]
107            fn as_ref(&self) -> &#integer {
108                self.as_primitive()
109            }
110        }
111    }
112}
113
114pub fn impl_conversions(name: &syn::Ident, params: &Params) -> TokenStream {
115    let integer = params.integer;
116    let mut conversions = Vec::with_capacity(24);
117
118    if params.is_u128_or_smaller() {
119        conversions.push(quote! {
120            impl From<#name> for u128 {
121                #[inline(always)]
122                fn from(val: #name ) -> Self {
123                    val.into_primitive() as u128
124                }
125            }
126        });
127    }
128
129    if matches!(params.integer, NumberKind::U128) {
130        conversions.push(quote! {
131            impl From<u128> for #name {
132                #[inline(always)]
133                fn from(val: u128) -> Self {
134                    Self::from_primitive(val).expect("value should be within bounds")
135                }
136            }
137        });
138    }
139
140    if params.is_usize_or_smaller() {
141        conversions.push(quote! {
142            impl From<#name> for usize {
143                #[inline(always)]
144                fn from(val: #name) -> Self {
145                    val.into_primitive() as usize
146                }
147            }
148        });
149    }
150
151    if params.is_usize_or_larger() {
152        conversions.push(quote! {
153            impl From<usize> for #name {
154                #[inline(always)]
155                fn from(val: usize) -> Self {
156                    Self::from_primitive(val as #integer).expect("value should be within bounds")
157                }
158            }
159        });
160    }
161
162    if params.is_u64_or_smaller() {
163        conversions.push(quote! {
164            impl From<#name> for u64 {
165                #[inline(always)]
166                fn from(val: #name) -> Self {
167                    val.into_primitive() as u64
168                }
169            }
170        });
171    }
172
173    if params.is_u64_or_larger() {
174        conversions.push(quote! {
175            impl From<u64> for #name {
176                #[inline(always)]
177                fn from(val: u64) -> Self {
178                    Self::from_primitive(val as #integer).expect("value should be within bounds")
179                }
180            }
181        });
182    }
183
184    if params.is_u32_or_smaller() {
185        conversions.push(quote! {
186            impl From<#name> for u32 {
187                #[inline(always)]
188                fn from(val: #name) -> Self {
189                    val.into_primitive() as u32
190                }
191            }
192        });
193    }
194
195    if params.is_u32_or_larger() {
196        conversions.push(quote! {
197            impl From<u32> for #name {
198                #[inline(always)]
199                fn from(val: u32) -> Self {
200                    Self::from_primitive(val as #integer).expect("value should be within bounds")
201                }
202            }
203        });
204    }
205
206    if params.is_u16_or_smaller() {
207        conversions.push(quote! {
208            impl From<#name> for u16 {
209                #[inline(always)]
210                fn from(val: #name) -> Self {
211                    val.into_primitive() as u16
212                }
213            }
214        });
215    }
216
217    if params.is_u16_or_larger() {
218        conversions.push(quote! {
219            impl From<u16> for #name {
220                #[inline(always)]
221                fn from(val: u16) -> Self {
222                    Self::from_primitive(val as #integer).expect("value should be within bounds")
223                }
224            }
225        });
226    }
227
228    if matches!(params.integer, NumberKind::U8) {
229        conversions.push(quote! {
230            impl From<#name> for u8 {
231                #[inline(always)]
232                fn from(val: #name) -> Self {
233                    val.into_primitive() as u8
234                }
235            }
236        });
237    }
238
239    if params.is_i128_or_smaller() {
240        conversions.push(quote! {
241            impl From<#name> for i128 {
242                #[inline(always)]
243                fn from(val: #name ) -> Self {
244                    val.into_primitive() as i128
245                }
246            }
247        });
248    }
249
250    if matches!(params.integer, NumberKind::I128) {
251        conversions.push(quote! {
252            impl From<i128> for #name {
253                #[inline(always)]
254                fn from(val: i128) -> Self {
255                    Self::from_primitive(val).expect("value should be within bounds")
256                }
257            }
258        });
259    }
260
261    if params.is_isize_or_smaller() {
262        conversions.push(quote! {
263            impl From<#name> for isize {
264                #[inline(always)]
265                fn from(val: #name) -> Self {
266                    val.into_primitive() as isize
267                }
268            }
269        });
270    }
271
272    if params.is_isize_or_larger() {
273        conversions.push(quote! {
274            impl From<isize> for #name {
275                #[inline(always)]
276                fn from(val: isize) -> Self {
277                    Self::from_primitive(val as #integer).expect("value should be within bounds")
278                }
279            }
280        });
281    }
282
283    if params.is_i64_or_smaller() {
284        conversions.push(quote! {
285            impl From<#name> for i64 {
286                #[inline(always)]
287                fn from(val: #name) -> Self {
288                    val.into_primitive() as i64
289                }
290            }
291        });
292    }
293
294    if params.is_i64_or_larger() {
295        conversions.push(quote! {
296            impl From<i64> for #name {
297                #[inline(always)]
298                fn from(val: i64) -> Self {
299                    Self::from_primitive(val as #integer).expect("value should be within bounds")
300                }
301            }
302        });
303    }
304
305    if params.is_i32_or_smaller() {
306        conversions.push(quote! {
307            impl From<#name> for i32 {
308                #[inline(always)]
309                fn from(val: #name) -> Self {
310                    val.into_primitive() as i32
311                }
312            }
313        });
314    }
315
316    if params.is_i32_or_larger() {
317        conversions.push(quote! {
318            impl From<i32> for #name {
319                #[inline(always)]
320                fn from(val: i32) -> Self {
321                    Self::from_primitive(val as #integer).expect("value should be within bounds")
322                }
323            }
324        });
325    }
326
327    if params.is_i16_or_smaller() {
328        conversions.push(quote! {
329            impl From<#name> for i16 {
330                #[inline(always)]
331                fn from(val: #name) -> Self {
332                    val.into_primitive() as i16
333                }
334            }
335        });
336    }
337
338    if params.is_i16_or_larger() {
339        conversions.push(quote! {
340            impl From<i16> for #name {
341                #[inline(always)]
342                fn from(val: i16) -> Self {
343                    Self::from_primitive(val as #integer).expect("value should be within bounds")
344                }
345            }
346        });
347    }
348
349    if matches!(params.integer, NumberKind::I8) {
350        conversions.push(quote! {
351            impl From<#name> for i8 {
352                #[inline(always)]
353                fn from(val: #name) -> Self {
354                    val.into_primitive() as i8
355                }
356            }
357        });
358    }
359
360    if params.is_signed() {
361        conversions.push(quote! {
362            impl From<i8> for #name {
363                #[inline(always)]
364                fn from(val: i8) -> Self {
365                    Self::from_primitive(val as #integer).expect("value should be within bounds")
366                }
367            }
368        });
369    } else {
370        conversions.push(quote! {
371            impl From<u8> for #name {
372                #[inline(always)]
373                fn from(val: u8) -> Self {
374                    Self::from_primitive(val as #integer).expect("value should be within bounds")
375                }
376            }
377        });
378    }
379
380    quote! {
381        #(#conversions)*
382
383        impl std::str::FromStr for #name {
384            type Err = ::anyhow::Error;
385
386            #[inline(always)]
387            fn from_str(s: &str) -> ::anyhow::Result<Self> {
388                let n = s.parse::<#integer>()?;
389                Self::from_primitive(n)
390            }
391        }
392    }
393}
394
395pub fn impl_self_eq(name: &syn::Ident) -> TokenStream {
396    quote! {
397        impl std::cmp::PartialEq<#name> for #name
398        {
399            #[inline(always)]
400            fn eq(&self, other: &#name ) -> bool {
401                self.into_primitive() == other.into_primitive()
402            }
403        }
404
405        impl std::cmp::Eq for #name
406        {
407        }
408    }
409}
410
411pub fn impl_self_cmp(name: &syn::Ident) -> TokenStream {
412    quote! {
413        impl std::cmp::PartialOrd<#name> for #name
414        {
415            #[inline(always)]
416            fn partial_cmp(&self, rhs: &#name ) -> Option<std::cmp::Ordering> {
417                self.into_primitive().partial_cmp(&rhs.into_primitive())
418            }
419        }
420
421        impl std::cmp::Ord for #name
422        {
423            #[inline(always)]
424            fn cmp(&self, rhs: &#name) -> std::cmp::Ordering {
425                self.into_primitive().cmp(&rhs.into_primitive())
426            }
427        }
428    }
429}
430
431pub fn impl_other_eq(name: &syn::Ident, params: &Params) -> TokenStream {
432    let integer = params.integer;
433
434    quote! {
435        impl std::cmp::PartialEq<#integer> for #name
436        {
437            #[inline(always)]
438            fn eq(&self, other: &#integer ) -> bool {
439                self.into_primitive() == *other
440            }
441        }
442
443        impl std::cmp::PartialEq<#name> for #integer
444        {
445            #[inline(always)]
446            fn eq(&self, other: &#name) -> bool {
447                *self == other.into_primitive()
448            }
449        }
450    }
451}
452
453pub fn impl_other_compare(name: &syn::Ident, params: &Params) -> TokenStream {
454    let integer = params.integer;
455
456    quote! {
457        impl std::cmp::PartialOrd<#integer> for #name
458        {
459            #[inline(always)]
460            fn partial_cmp(&self, other: &#integer ) -> Option<std::cmp::Ordering> {
461                (self.into_primitive()).partial_cmp(other)
462            }
463        }
464
465        impl std::cmp::PartialOrd<#name> for #integer
466        {
467            #[inline(always)]
468            fn partial_cmp(&self, other: &#name) -> Option<std::cmp::Ordering> {
469                self.partial_cmp(other.as_primitive())
470            }
471        }
472    }
473}
474
475pub fn impl_binary_op(
476    name: &syn::Ident,
477    params: &Params,
478    trait_name: syn::Ident,
479    method_name: syn::Ident,
480    behavior: &BehaviorArg,
481    explicit_bounds: Option<(NumberArg, NumberArg)>,
482) -> TokenStream {
483    let integer = params.integer;
484    let assign_trait_name = format_ident!("{}Assign", trait_name);
485    let assign_method_name = format_ident!("{}_assign", method_name);
486
487    let op_params = if let Some((lower, upper)) = explicit_bounds {
488        quote! {
489            OpBehaviorParams::Simple {
490                min: #lower,
491                max: #upper,
492            }
493        }
494    } else {
495        quote! {
496            self.op_behavior_params()
497        }
498    };
499
500    quote! {
501        impl std::ops::#trait_name for #name {
502            type Output = #name;
503
504            #[inline(always)]
505            fn #method_name(self, rhs: #name) -> #name {
506                unsafe {
507                    Self::from_primitive_unchecked(#behavior::#method_name(
508                        self.into_primitive(),
509                        rhs.into_primitive(),
510                        #op_params
511                    ))
512                }
513            }
514        }
515
516        impl std::ops::#trait_name<#integer> for #name {
517            type Output = #name;
518
519            #[inline(always)]
520            fn #method_name(self, rhs: #integer) -> #name {
521                unsafe {
522                    Self::from_primitive_unchecked(#behavior::#method_name(
523                        self.into_primitive(),
524                        rhs,
525                        #op_params
526                    ))
527                }
528            }
529        }
530
531        impl std::ops::#trait_name<#name> for #integer {
532            type Output = #integer;
533
534            #[inline(always)]
535            fn #method_name(self, rhs: #name) -> #integer {
536                Panicking::#method_name(self, rhs.into_primitive(), OpBehaviorParams::Simple {
537                    min: #integer::MIN,
538                    max: #integer::MAX,
539                })
540            }
541        }
542
543        impl std::ops::#trait_name<#name> for std::num::Saturating<#integer> {
544            type Output = std::num::Saturating<#integer>;
545
546            #[inline(always)]
547            fn #method_name(self, rhs: #name) -> std::num::Saturating<#integer> {
548                std::num::Saturating(Saturating::#method_name(self.0, rhs.into_primitive(), OpBehaviorParams::Simple {
549                    min: #integer::MIN,
550                    max: #integer::MAX,
551                }))
552            }
553        }
554
555        impl std::ops::#assign_trait_name for #name {
556            #[inline(always)]
557            fn #assign_method_name(&mut self, rhs: #name) {
558                *self = unsafe {
559                    Self::from_primitive_unchecked(#behavior::#method_name(
560                        self.into_primitive(),
561                        rhs.into_primitive(),
562                        #op_params
563                    ))
564                };
565            }
566        }
567
568        impl std::ops::#assign_trait_name<#integer> for #name {
569            #[inline(always)]
570            fn #assign_method_name(&mut self, rhs: #integer) {
571                *self = unsafe {
572                    Self::from_primitive_unchecked(#behavior::#method_name(
573                        self.into_primitive(),
574                        rhs,
575                        #op_params
576                    ))
577                };
578            }
579        }
580
581        impl std::ops::#assign_trait_name<#name> for #integer {
582            #[inline(always)]
583            fn #assign_method_name(&mut self, rhs: #name) {
584                *self = Panicking::#method_name(
585                    *self,
586                    rhs.into_primitive(),
587                    OpBehaviorParams::Simple {
588                        min: #integer::MIN,
589                        max: #integer::MAX,
590                    }
591                );
592            }
593        }
594
595        impl std::ops::#assign_trait_name<#name> for std::num::Saturating<#integer> {
596            #[inline(always)]
597            fn #assign_method_name(&mut self, rhs: #name) {
598                *self = std::num::Saturating(Saturating::#method_name(
599                    self.0,
600                    rhs.into_primitive(),
601                    OpBehaviorParams::Simple {
602                        min: #integer::MIN,
603                        max: #integer::MAX,
604                    }
605                ));
606            }
607        }
608    }
609}