sqlx_pg_uint_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput};
4
5#[proc_macro_derive(UIntWrapper)]
6/// Derive macro for unsigned integer types.
7///
8/// Derives all the mathematical operations for the unsigned integer type, as well as `Display`,
9/// `From` and `TryFrom` implementations for/to `BigDecimal`, a `to_uint` method to convert the
10/// `PgUint` type to the underlying integer type and a `new` method to create a new `PgUint` type
11/// from the underlying integer type.
12pub fn uint_wrapper_derive(input: TokenStream) -> TokenStream {
13    let input = parse_macro_input!(input as DeriveInput);
14    let name = &input.ident;
15
16    let gen = quote! {
17        impl std::fmt::Display for #name {
18            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
19                write!(f, "{}", self.to_uint())
20            }
21        }
22
23        impl std::ops::Add for #name {
24            type Output = Self;
25
26            fn add(self, rhs: Self) -> Self::Output {
27                Self::new(self.to_uint() + rhs.to_uint())
28            }
29        }
30
31        impl std::ops::Mul for #name {
32            type Output = Self;
33
34            fn mul(self, rhs: Self) -> Self::Output {
35                Self::new(self.to_uint() * rhs.to_uint())
36            }
37        }
38
39        impl std::ops::Sub for #name {
40            type Output = Self;
41
42            fn sub(self, rhs: Self) -> Self::Output {
43                Self::new(self.to_uint() - rhs.to_uint())
44            }
45        }
46
47        impl std::ops::Div for #name {
48            type Output = Self;
49
50            fn div(self, rhs: Self) -> Self::Output {
51                Self::new(self.to_uint() / rhs.to_uint())
52            }
53        }
54
55        impl std::ops::Rem for #name {
56            type Output = Self;
57
58            fn rem(self, rhs: Self) -> Self::Output {
59                Self::new(self.to_uint() % rhs.to_uint())
60            }
61        }
62
63        impl std::ops::AddAssign for #name {
64            fn add_assign(&mut self, rhs: Self) {
65                let inner = BigDecimal::from(self.to_uint() + rhs.to_uint());
66                *self = Self { inner };
67            }
68        }
69
70        impl std::ops::SubAssign for #name {
71            fn sub_assign(&mut self, rhs: Self) {
72                let inner = BigDecimal::from(self.to_uint() - rhs.to_uint());
73                *self = Self { inner };
74            }
75        }
76
77        impl std::ops::MulAssign for #name {
78            fn mul_assign(&mut self, rhs: Self) {
79                let inner = BigDecimal::from(self.to_uint() * rhs.to_uint());
80                *self = Self { inner };
81            }
82        }
83
84        impl std::ops::DivAssign for #name {
85            fn div_assign(&mut self, rhs: Self) {
86                let inner = BigDecimal::from(self.to_uint() / rhs.to_uint());
87                *self = Self { inner };
88            }
89        }
90
91        impl std::str::FromStr for #name {
92            type Err = Error;
93
94            fn from_str(s: &str) -> Result<Self, Self::Err> {
95                let unsigned_int: <Self as UIntType>::Uint = s.parse()?;
96                Ok(Self::from(unsigned_int))
97            }
98        }
99
100        impl #name {
101            /// Converts this type to the associated unsigned integer type
102            pub fn to_uint(&self) -> <#name as UIntType>::Uint {
103                let stringed_num = self.inner.to_string();
104                stringed_num.parse().unwrap()
105            }
106
107            /// Converts `Option<PgUint>` to `Option<[underlying integer type]>`.
108            pub fn to_option_uint(&self) -> Option<<#name as UIntType>::Uint> {
109                <Option<#name> as OptionPgUint<#name>>::to_option_uint(&Some(self.clone()))
110            }
111
112            /// Creates a new instance of this type from the associated unsigned integer type
113            pub fn new(num: <#name as UIntType>::Uint) -> Self {
114                Self {
115                    inner: BigDecimal::from(num),
116                }
117            }
118
119            /// Returns a shared reference to the inner `BigDecimal` value
120            pub fn as_big_decimal(&self) -> &BigDecimal {
121                &self.inner
122            }
123        }
124
125        impl OptionPgUint<#name> for Option<#name> where #name: UIntType {
126            fn to_option_uint(&self) -> Option<<#name as UIntType>::Uint> {
127                self.clone().map(|v| v.to_uint())
128            }
129        }
130
131        impl TryFrom<BigDecimal> for #name {
132            type Error = crate::Error;
133
134            fn try_from(value: BigDecimal) -> Result<Self, Self::Error> {
135                let value_ref = &value;
136                if !value_ref.is_integer() {
137                    return Err(crate::Error::Fractional(value));
138                }
139                if value_ref.to_string().parse::<u128>().is_err() {
140                    return Err(crate::Error::InvalidValue(value));
141                }
142                Ok(Self { inner: value })
143            }
144        }
145
146        impl From<#name> for BigDecimal {
147            fn from(value: #name) -> Self {
148                value.inner
149            }
150        }
151
152        impl Default for #name {
153            fn default() -> Self {
154                Self::from(0)
155            }
156        }
157
158        impl sqlx::Type<sqlx::Postgres> for #name {
159            fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo {
160                <BigDecimal as sqlx::Type<sqlx::Postgres>>::type_info()
161            }
162        }
163
164        impl<'q> sqlx::Encode<'q, sqlx::Postgres> for #name {
165            fn encode_by_ref(
166                &self,
167                buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>,
168            ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
169                <BigDecimal as sqlx::Encode<sqlx::Postgres>>::encode_by_ref(&self.inner, buf)
170            }
171        }
172
173        impl<'r> sqlx::Decode<'r, sqlx::Postgres> for #name {
174            fn decode(
175                value: <sqlx::Postgres as sqlx::Database>::ValueRef<'r>,
176            ) -> Result<Self, sqlx::error::BoxDynError> {
177                let big_decimal = <BigDecimal as sqlx::Decode<sqlx::Postgres>>::decode(value)
178                ?;
179                Ok(#name::try_from(big_decimal)?)
180            }
181        }
182
183        impl sqlx::postgres::PgHasArrayType for #name {
184            fn array_type_info() -> sqlx::postgres::PgTypeInfo {
185                <Vec<BigDecimal> as sqlx::Type<sqlx::Postgres>>::type_info()
186            }
187        }
188
189        #[cfg(feature = "serde")]
190        impl serde::ser::Serialize for #name {
191            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
192            where
193                S: serde::Serializer,
194            {
195                self.to_uint().serialize(serializer)
196            }
197        }
198
199        #[cfg(feature = "serde")]
200        impl<'de> serde::de::Deserialize<'de> for #name {
201            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
202            where
203                D: serde::Deserializer<'de>,
204            {
205                let big_decimal = BigDecimal::deserialize(deserializer)?;
206                #name::try_from(big_decimal).map_err(serde::de::Error::custom)
207            }
208        }
209    };
210
211    gen.into()
212}