1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! Перобразование примитивных числовых типов через метод трейта [`AsPrim`] (замена опреатора `as`).
//!
//! # Стандартная реализация с оператором `as`.
//! Изначально rust предоставляет нам возможность преобразования примитивных типов с помощью опреатора `as`.
//! В следующих примерах показано, как опреатор `as` может сбивать с толку из-за своей семнтики:
//! ```
//! use core::f64::consts::PI;
//!
//! let radius = 10_i32;
//! let circle_area = 2 as f64 * PI * radius as f64;
//! ```
//! Даже скобки не спасают:
//! ```
//! # use core::f64::consts::PI;
//! # let radius = 10_i32;
//! let circle_area = (2 as f64) * PI * (radius as f64);
//! ```
//! # Реализация AsPrim
//!
//! Трейт [`AsPrim`] и вспомогательные ему [`FromPrim`] и [`ToPrim`] преднозначены для того, чтобы добиться функционального стиля в преобразовании примитивных типов без оператора `as`.
//!
//! ```
//! # use std_reset::prelude::AsPrim;
//! # use core::f64::consts::PI;
//! # let radius = 10_i32;
//! let circle_area = 2.as_::<f64>() * PI * radius.as_::<f64>();
//! let vec: Vec<f32> = vec![2.as_(), circle_area.as_(), 4_isize.as_()];
//! ```
//!
//! Также вы можете использовать [`FromPrim`], как будто используете [`From`]:
//! ```
//! # use std_reset::traits::as_prim::FromPrim;
//! let num = f64::as_from(10_i32);
//!
//! ```
//! Или  использовать [`ToPrim`] для преобразования напрямую:
//! ```
//! # use std_reset::traits::as_prim::ToPrim;
//! let num = 2.to_f32();
//! ```

use paste::paste;

macro_rules! every_type_method {
     ($($t:ty),+) => {
         $(
             paste! {
                 fn [<to_ $t>](self) -> $t {
                     self as $t
                 }
             }
         )+
     };
 }

macro_rules! impl_for_every_num_types {
     ($($t:ty),+) => {
         pub trait ToPrim: ToString {
             $(
                 paste! {
                     fn [<to_ $t>](self) -> $t;
                 }
             )+
         }
         $(
             impl ToPrim for $t {
                 every_type_method!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64);
             }
         )+
         $(
             impl FromPrim for $t {
                 paste! {
                     fn as_from<F: ToPrim>(value: F) -> $t {
                         value.[<to_ $t>]()
                     }
                 }
             }
         )+
     }
 }

impl_for_every_num_types!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64);
pub trait FromPrim: ToPrim {
    fn as_from<F: ToPrim>(value: F) -> Self;
}

pub trait AsPrim: FromPrim {
    fn as_<I: FromPrim>(self) -> I;
}

impl<F: FromPrim> AsPrim for F {
    fn as_<I: FromPrim>(self) -> I {
        I::as_from::<F>(self)
    }
}