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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/// Adds conversions `from` and `into` integer to enums
///
/// Takes an enum that must have a `#[repr()]` as first meta field and assigns a
/// value to all enum variants.
///
/// ```
/// # use gufo_common::utils::convertible_enum;
/// convertible_enum!(
///     #[repr(u8)]
///     #[derive(Debug, PartialEq)]
///     pub enum Test {
///         Val1 = 1,
///         Val2 = 2,
///     }
/// );
/// let int: u8 = Test::Val2.into();
/// assert_eq!(int, 2);
/// assert_eq!(Test::from(2), Test::Val2);
/// assert_eq!(Test::from(3), Test::Unknown(3));
/// ```
#[macro_export]
macro_rules! convertible_enum {
    (#[repr($type:ty)]$(#[$meta:meta])* $visibility:vis enum $enum_name:ident {
        $($(#[$variant_meta:meta])* $variant_name:ident = $variant_value:expr,)*
    }) => {
        #[repr($type)]
        $(#[$meta])*
        $visibility enum $enum_name {
            $($(#[$variant_meta])* $variant_name = $variant_value,)*
            Unknown($type)
        }

        impl std::convert::From<$type> for $enum_name {
            fn from(v: $type) -> Self {
                match v {
                    $($variant_value => Self::$variant_name,)*
                    other => Self::Unknown(other),
                }
            }
        }

        impl std::convert::Into<$type> for $enum_name {
            fn into(self) -> $type {
                match self {
                    $(Self::$variant_name => $variant_value,)*
                    Self::Unknown(other) => other,
                }
            }
        }
    }
}

/// Adds conversions `try_from` and `into` integer to enums
///
/// Takes an enum that must have a `#[repr()]` as first meta field and assigns a
/// value to all enum variants.
///
/// ```
/// # use gufo_common::utils::maybe_convertible_enum;
/// maybe_convertible_enum!(
///     #[repr(u8)]
///     #[derive(Debug, PartialEq)]
///     pub enum Test {
///         Val1 = 1,
///         Val2 = 2,
///     }
/// );
/// let int: u8 = Test::Val2.into();
/// assert_eq!(int, 2);
/// assert_eq!(Test::try_from(2), Ok(Test::Val2));
/// assert_eq!(Test::try_from(3), Err(UnknownTestValueError(3)));
/// ```
#[macro_export]
macro_rules! maybe_convertible_enum {
    (#[repr($type:ty)]$(#[$meta:meta])* $visibility:vis enum $enum_name:ident {
        $($(#[$variant_meta:meta])* $variant_name:ident = $variant_value:expr,)*
    }) => {
        #[repr($type)]
        $(#[$meta])*
        $visibility enum $enum_name {
            $($(#[$variant_meta])* $variant_name = $variant_value,)*
        }

        paste::paste! {
            #[derive(Debug, PartialEq, Eq)]
            pub struct [<Unknown $enum_name ValueError>]($type);

            impl std::fmt::Display for [<Unknown $enum_name ValueError>] {
                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                    write!(f, concat!("Enum '", stringify!($enum_name), "' has no variant with value '{}'"), self.0)
                }
            }

            impl std::error::Error for [<Unknown $enum_name ValueError>] {}

            /// Create enum from it's discriminant value
            impl std::convert::TryFrom<$type> for $enum_name {
                type Error =  [<Unknown $enum_name ValueError>];
                fn try_from(v: $type) -> Result<Self, Self::Error> {
                    match v {
                        $($variant_value => Ok(Self::$variant_name),)*
                        other => Err([<Unknown $enum_name ValueError>](other)),
                    }
                }
            }
        }

        /// Convert enum to it's discriminant value
        impl From<$enum_name> for $type {
            fn from(v: $enum_name) -> $type {
                match v {
                    $($enum_name::$variant_name => $variant_value,)*
                }
            }
        }
    }
}

pub use {convertible_enum, maybe_convertible_enum};

#[derive(Debug, PartialEq, Eq)]
pub struct AdditionOverflowError;
#[derive(Debug, PartialEq, Eq)]
pub struct SubstractionOverflowError;

pub trait U32Ext {
    fn usize(self) -> usize;
    fn i64(self) -> i64;
    fn safe_add(self, rhs: u32) -> Result<u32, AdditionOverflowError>;
    fn safe_sub(self, rhs: u32) -> Result<u32, SubstractionOverflowError>;
}

impl U32Ext for u32 {
    fn usize(self) -> usize {
        // Assume that systems are at least 32bit
        self.try_into().unwrap()
    }

    fn i64(self) -> i64 {
        self.into()
    }

    fn safe_add(self, rhs: u32) -> Result<u32, AdditionOverflowError> {
        self.checked_add(rhs).ok_or(AdditionOverflowError)
    }

    fn safe_sub(self, rhs: u32) -> Result<u32, SubstractionOverflowError> {
        self.checked_sub(rhs).ok_or(SubstractionOverflowError)
    }
}

#[derive(Debug, PartialEq, Eq)]
pub struct ConversionOverflowError;

pub trait I64Ext {
    fn u32(self) -> Result<u32, ConversionOverflowError>;
    fn safe_add(self, rhs: i64) -> Result<i64, AdditionOverflowError>;
}

impl I64Ext for i64 {
    fn u32(self) -> Result<u32, ConversionOverflowError> {
        self.try_into().map_err(|_| ConversionOverflowError)
    }

    fn safe_add(self, rhs: i64) -> Result<i64, AdditionOverflowError> {
        self.checked_add(rhs).ok_or(AdditionOverflowError)
    }
}