1use crate::postgres::common::*;
2use crate::Decimal;
3use diesel::{
4 deserialize::{self, FromSql},
5 pg::data_types::PgNumeric,
6 pg::Pg,
7 serialize::{self, Output, ToSql},
8 sql_types::Numeric,
9};
10use std::error;
11
12impl<'a> TryFrom<&'a PgNumeric> for Decimal {
13 type Error = Box<dyn error::Error + Send + Sync>;
14
15 fn try_from(numeric: &'a PgNumeric) -> deserialize::Result<Self> {
16 let (neg, weight, scale, digits) = match *numeric {
17 PgNumeric::Positive {
18 weight,
19 scale,
20 ref digits,
21 } => (false, weight, scale, digits),
22 PgNumeric::Negative {
23 weight,
24 scale,
25 ref digits,
26 } => (true, weight, scale, digits),
27 PgNumeric::NaN => return Err(Box::from("NaN is not supported in Decimal")),
28 };
29
30 let Some(result) = Self::checked_from_postgres(PostgresDecimal {
31 neg,
32 weight,
33 scale,
34 digits: digits.iter().copied().map(|v| v.try_into().unwrap()),
35 }) else {
36 return Err(Box::new(crate::error::Error::ExceedsMaximumPossibleValue));
37 };
38 Ok(result)
39 }
40}
41
42impl TryFrom<PgNumeric> for Decimal {
43 type Error = Box<dyn error::Error + Send + Sync>;
44
45 fn try_from(numeric: PgNumeric) -> deserialize::Result<Self> {
46 (&numeric).try_into()
47 }
48}
49
50impl<'a> From<&'a Decimal> for PgNumeric {
51 fn from(decimal: &'a Decimal) -> Self {
52 let PostgresDecimal {
53 neg,
54 weight,
55 scale,
56 digits,
57 } = decimal.to_postgres();
58
59 if neg {
60 PgNumeric::Negative { digits, scale, weight }
61 } else {
62 PgNumeric::Positive { digits, scale, weight }
63 }
64 }
65}
66
67impl From<Decimal> for PgNumeric {
68 fn from(decimal: Decimal) -> Self {
69 (&decimal).into()
70 }
71}
72
73#[cfg(feature = "diesel")]
74impl ToSql<Numeric, Pg> for Decimal {
75 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
76 let numeric = PgNumeric::from(self);
77 ToSql::<Numeric, Pg>::to_sql(&numeric, &mut out.reborrow())
78 }
79}
80
81#[cfg(feature = "diesel")]
82impl FromSql<Numeric, Pg> for Decimal {
83 fn from_sql(numeric: diesel::pg::PgValue) -> deserialize::Result<Self> {
84 PgNumeric::from_sql(numeric)?.try_into()
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use core::str::FromStr;
92
93 #[test]
94 fn test_unnecessary_zeroes() {
95 fn extract(value: &str) -> Decimal {
96 Decimal::from_str(value).unwrap()
97 }
98
99 let tests = &[
100 ("0.000001660"),
101 ("41.120255926293000"),
102 ("0.5538973300"),
103 ("08883.55986854293100"),
104 ("0.0000_0000_0016_6000_00"),
105 ("0.00000166650000"),
106 ("1666500000000"),
107 ("1666500000000.0000054500"),
108 ("8944.000000000000"),
109 ];
110
111 for &value in tests {
112 let value = extract(value);
113 let pg = PgNumeric::from(value);
114 let dec = Decimal::try_from(pg).unwrap();
115 assert_eq!(dec, value);
116 }
117 }
118
119 #[test]
120 fn decimal_to_pgnumeric_converts_digits_to_base_10000() {
121 let decimal = Decimal::from_str("1").unwrap();
122 let expected = PgNumeric::Positive {
123 weight: 0,
124 scale: 0,
125 digits: vec![1],
126 };
127 assert_eq!(expected, decimal.into());
128
129 let decimal = Decimal::from_str("10").unwrap();
130 let expected = PgNumeric::Positive {
131 weight: 0,
132 scale: 0,
133 digits: vec![10],
134 };
135 assert_eq!(expected, decimal.into());
136
137 let decimal = Decimal::from_str("10000").unwrap();
138 let expected = PgNumeric::Positive {
139 weight: 1,
140 scale: 0,
141 digits: vec![1, 0],
142 };
143 assert_eq!(expected, decimal.into());
144
145 let decimal = Decimal::from_str("10001").unwrap();
146 let expected = PgNumeric::Positive {
147 weight: 1,
148 scale: 0,
149 digits: vec![1, 1],
150 };
151 assert_eq!(expected, decimal.into());
152
153 let decimal = Decimal::from_str("100000000").unwrap();
154 let expected = PgNumeric::Positive {
155 weight: 2,
156 scale: 0,
157 digits: vec![1, 0, 0],
158 };
159 assert_eq!(expected, decimal.into());
160 }
161
162 #[test]
163 fn decimal_to_pg_numeric_properly_adjusts_scale() {
164 let decimal = Decimal::from_str("1").unwrap();
165 let expected = PgNumeric::Positive {
166 weight: 0,
167 scale: 0,
168 digits: vec![1],
169 };
170 assert_eq!(expected, decimal.into());
171
172 let decimal = Decimal::from_str("1.0").unwrap();
173 let expected = PgNumeric::Positive {
174 weight: 0,
175 scale: 1,
176 digits: vec![1],
177 };
178 assert_eq!(expected, decimal.into());
179
180 let decimal = Decimal::from_str("1.1").unwrap();
181 let expected = PgNumeric::Positive {
182 weight: 0,
183 scale: 1,
184 digits: vec![1, 1000],
185 };
186 assert_eq!(expected, decimal.into());
187
188 let decimal = Decimal::from_str("1.10").unwrap();
189 let expected = PgNumeric::Positive {
190 weight: 0,
191 scale: 2,
192 digits: vec![1, 1000],
193 };
194 assert_eq!(expected, decimal.into());
195
196 let decimal = Decimal::from_str("100000000.0001").unwrap();
197 let expected = PgNumeric::Positive {
198 weight: 2,
199 scale: 4,
200 digits: vec![1, 0, 0, 1],
201 };
202 assert_eq!(expected, decimal.into());
203
204 let decimal = Decimal::from_str("0.1").unwrap();
205 let expected = PgNumeric::Positive {
206 weight: -1,
207 scale: 1,
208 digits: vec![1000],
209 };
210 assert_eq!(expected, decimal.into());
211 }
212
213 #[test]
214 fn decimal_to_pg_numeric_retains_sign() {
215 let decimal = Decimal::from_str("123.456").unwrap();
216 let expected = PgNumeric::Positive {
217 weight: 0,
218 scale: 3,
219 digits: vec![123, 4560],
220 };
221 assert_eq!(expected, decimal.into());
222
223 let decimal = Decimal::from_str("-123.456").unwrap();
224 let expected = PgNumeric::Negative {
225 weight: 0,
226 scale: 3,
227 digits: vec![123, 4560],
228 };
229 assert_eq!(expected, decimal.into());
230 }
231
232 #[test]
233 fn pg_numeric_to_decimal_works() {
234 let expected = Decimal::from_str("50").unwrap();
235 let pg_numeric = PgNumeric::Positive {
236 weight: 0,
237 scale: 0,
238 digits: vec![50],
239 };
240 let res: Decimal = pg_numeric.try_into().unwrap();
241 assert_eq!(res, expected);
242 let expected = Decimal::from_str("123.456").unwrap();
243 let pg_numeric = PgNumeric::Positive {
244 weight: 0,
245 scale: 3,
246 digits: vec![123, 4560],
247 };
248 let res: Decimal = pg_numeric.try_into().unwrap();
249 assert_eq!(res, expected);
250
251 let expected = Decimal::from_str("-56.78").unwrap();
252 let pg_numeric = PgNumeric::Negative {
253 weight: 0,
254 scale: 2,
255 digits: vec![56, 7800],
256 };
257 let res: Decimal = pg_numeric.try_into().unwrap();
258 assert_eq!(res, expected);
259
260 let expected = Decimal::from_str("1.100").unwrap();
263 let pg_numeric = PgNumeric::Positive {
264 weight: 0,
265 scale: 3,
266 digits: vec![1, 1000],
267 };
268 let res: Decimal = pg_numeric.try_into().unwrap();
269 assert_eq!(res.to_string(), expected.to_string());
270
271 let expected = Decimal::from_str("5.00").unwrap();
273 let pg_numeric = PgNumeric::Positive {
274 weight: 0,
275 scale: 2,
276
277 digits: vec![5, 0],
278 };
279 let res: Decimal = pg_numeric.try_into().unwrap();
280 assert_eq!(res.to_string(), expected.to_string());
281
282 let expected = Decimal::from_str("5.00").unwrap();
284 let pg_numeric = PgNumeric::Positive {
285 weight: 0,
286 scale: 2,
287 digits: vec![5],
288 };
289 let res: Decimal = pg_numeric.try_into().unwrap();
290 assert_eq!(res.to_string(), expected.to_string());
291
292 let expected = Decimal::from_str("3.1415926535897932384626433833").unwrap();
293 let pg_numeric = PgNumeric::Positive {
294 weight: 0,
295 scale: 30,
296 digits: vec![3, 1415, 9265, 3589, 7932, 3846, 2643, 3832, 7950, 2800],
297 };
298 let res: Decimal = pg_numeric.try_into().unwrap();
299 assert_eq!(res.to_string(), expected.to_string());
300
301 let expected = Decimal::from_str("3.1415926535897932384626433833").unwrap();
302 let pg_numeric = PgNumeric::Positive {
303 weight: 0,
304 scale: 34,
305 digits: vec![3, 1415, 9265, 3589, 7932, 3846, 2643, 3832, 7950, 2800],
306 };
307
308 let res: Decimal = pg_numeric.try_into().unwrap();
309 assert_eq!(res.to_string(), expected.to_string());
310
311 let expected = Decimal::from_str("1.2345678901234567890123456790").unwrap();
312 let pg_numeric = PgNumeric::Positive {
313 weight: 0,
314 scale: 34,
315 digits: vec![1, 2345, 6789, 0123, 4567, 8901, 2345, 6789, 5000, 0],
316 };
317
318 let res: Decimal = pg_numeric.try_into().unwrap();
319 assert_eq!(res.to_string(), expected.to_string());
320 }
321}