checked_rs_macro_impl/
common_impl.rs1use 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}