1use std::{borrow::Cow, fmt};
3
4use rust_decimal::Decimal;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6
7use crate::{
8 into_caveat, json,
9 warning::{self, IntoCaveat},
10};
11
12const DECIMAL_SCALE: u32 = 4;
14
15#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
17pub enum WarningKind {
18 ContainsEscapeCodes,
20
21 ExceedsMaximumPossibleValue,
23
24 Internal(String),
26
27 InvalidType,
29
30 LessThanMinimumPossibleValue,
32
33 Underflow,
35}
36
37impl fmt::Display for WarningKind {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match self {
40 WarningKind::ContainsEscapeCodes => write!(
41 f,
42 "The value contains escape codes but it does not need them"
43 ),
44 WarningKind::InvalidType => write!(f, "The value should be a number."),
45 WarningKind::ExceedsMaximumPossibleValue => {
46 write!(
47 f,
48 "The value provided exceeds `79,228,162,514,264,337,593,543,950,335`."
49 )
50 }
51 WarningKind::LessThanMinimumPossibleValue => write!(
52 f,
53 "The value provided is less than `-79,228,162,514,264,337,593,543,950,335`."
54 ),
55 WarningKind::Underflow => write!(
56 f,
57 "An underflow is when there are more than 28 fractional digits"
58 ),
59 WarningKind::Internal(msg) => write!(f, "{msg}"),
60 }
61 }
62}
63
64impl warning::Kind for WarningKind {
65 fn id(&self) -> Cow<'static, str> {
66 match self {
67 WarningKind::ContainsEscapeCodes => "contains_escape_codes".into(),
68 WarningKind::InvalidType => "invalid_type".into(),
69 WarningKind::ExceedsMaximumPossibleValue => "exceeds_maximum_possible_value".into(),
70 WarningKind::LessThanMinimumPossibleValue => "less_than_minimum_possible_value".into(),
71 WarningKind::Underflow => "underflow".into(),
72 WarningKind::Internal(_) => "<internal_error>".into(),
73 }
74 }
75}
76#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
80pub struct Number(Decimal);
81
82impl json::FromJson<'_, '_> for Number {
83 type WarningKind = WarningKind;
84
85 fn from_json(elem: &json::Element<'_>) -> crate::Verdict<Self, Self::WarningKind> {
86 let mut warnings = warning::Set::new();
87 let value = elem.as_value();
88
89 let Some(s) = value.as_number() else {
90 warnings.with_elem(WarningKind::InvalidType, elem);
91 return Err(warnings);
92 };
93
94 let mut decimal = match Decimal::from_str_exact(s) {
95 Ok(v) => v,
96 Err(err) => {
97 let kind = match err {
98 rust_decimal::Error::ConversionTo(msg)
99 | rust_decimal::Error::ErrorString(msg) => WarningKind::Internal(msg),
100 rust_decimal::Error::ExceedsMaximumPossibleValue => {
101 WarningKind::ExceedsMaximumPossibleValue
102 }
103 rust_decimal::Error::LessThanMinimumPossibleValue => {
104 WarningKind::LessThanMinimumPossibleValue
105 }
106 rust_decimal::Error::Underflow => WarningKind::Underflow,
107 rust_decimal::Error::ScaleExceedsMaximumPrecision(_) => {
108 WarningKind::Internal(err.to_string())
109 }
110 };
111
112 warnings.with_elem(kind, elem);
113 return Err(warnings);
114 }
115 };
116
117 decimal.rescale(DECIMAL_SCALE);
118 Ok(Self(decimal).into_caveat(warnings))
119 }
120}
121
122into_caveat!(Number);
123
124impl Number {
125 pub(crate) fn from_decimal(d: Decimal) -> Self {
126 Self(d)
127 }
128
129 pub fn is_zero(&self) -> bool {
130 self.0.is_zero()
131 }
132
133 #[must_use]
134 pub fn ceil(self) -> Self {
135 Self(self.0.ceil())
136 }
137
138 #[must_use]
140 pub fn rescale(mut self) -> Self {
141 self.0.rescale(DECIMAL_SCALE);
142 self
143 }
144
145 pub fn checked_div(self, other: Self) -> Option<Self> {
146 self.0.checked_div(other.0).map(Self)
147 }
148
149 #[must_use]
150 pub fn saturating_sub(self, other: Self) -> Self {
151 Self(self.0.saturating_sub(other.0))
152 }
153
154 #[must_use]
155 pub fn saturating_add(self, other: Self) -> Self {
156 Self(self.0.saturating_add(other.0))
157 }
158
159 #[must_use]
160 pub fn saturating_mul(self, other: Self) -> Self {
161 Self(self.0.saturating_mul(other.0))
162 }
163
164 #[must_use]
165 pub fn round_dp(self, digits: u32) -> Self {
166 Self(self.0.round_dp(digits))
167 }
168}
169
170impl From<Number> for Decimal {
171 fn from(value: Number) -> Self {
172 value.0
173 }
174}
175
176impl<'de> Deserialize<'de> for Number {
177 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
178 where
179 D: Deserializer<'de>,
180 {
181 let mut decimal = <Decimal as Deserialize>::deserialize(deserializer)?;
182 decimal.rescale(DECIMAL_SCALE);
183 Ok(Self(decimal))
184 }
185}
186
187impl Serialize for Number {
188 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
189 where
190 S: Serializer,
191 {
192 let mut decimal = self.0;
193
194 decimal.rescale(DECIMAL_SCALE);
195 decimal.normalize_assign();
196
197 Serialize::serialize(&decimal, serializer)
198 }
199}
200
201impl From<Decimal> for Number {
202 fn from(value: Decimal) -> Self {
203 Self(value)
204 }
205}
206
207impl From<i64> for Number {
208 fn from(value: i64) -> Self {
209 Self(value.into())
210 }
211}
212
213impl From<u64> for Number {
214 fn from(value: u64) -> Self {
215 Self(value.into())
216 }
217}
218
219impl From<i32> for Number {
220 fn from(value: i32) -> Self {
221 Self(value.into())
222 }
223}
224
225impl TryFrom<Number> for i64 {
226 type Error = rust_decimal::Error;
227
228 fn try_from(value: Number) -> Result<Self, Self::Error> {
229 value.0.try_into()
230 }
231}
232
233impl fmt::Display for Number {
234 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235 self.0.fmt(f)
236 }
237}
238
239#[cfg(test)]
240mod test {
241 use rust_decimal::Decimal;
242
243 use crate::test::AsDecimal;
244
245 use super::Number;
246
247 impl AsDecimal for Number {
248 fn as_dec(&self) -> &Decimal {
249 &self.0
250 }
251 }
252}