rust_fixed_point_decimal/
from_str.rs1use std::{cmp::Ordering, convert::TryFrom, str::FromStr};
11
12use rust_fixed_point_decimal_core::{checked_mul_pow_ten, dec_repr_from_str};
13
14use crate::{Decimal, ParseDecimalError, PrecLimitCheck, True, MAX_PREC};
15
16impl<const P: u8> FromStr for Decimal<P>
17where
18 PrecLimitCheck<{ P <= MAX_PREC }>: True,
19{
20 type Err = ParseDecimalError;
21
22 fn from_str(lit: &str) -> Result<Self, Self::Err> {
58 let prec = P as isize;
59 let (coeff, exponent) = dec_repr_from_str(lit)?;
60 if exponent > 0 {
61 let shift = prec + exponent;
62 if shift > 38 {
63 return Result::Err(ParseDecimalError::MaxValueExceeded);
65 }
66 match checked_mul_pow_ten(coeff, shift as u8) {
67 None => Result::Err(ParseDecimalError::MaxValueExceeded),
68 Some(coeff) => Ok(Self::new_raw(coeff)),
69 }
70 } else {
71 let n_frac_digits = -exponent;
72 match n_frac_digits.cmp(&prec) {
73 Ordering::Equal => Ok(Self::new_raw(coeff)),
74 Ordering::Less => {
75 let shift = prec - n_frac_digits;
76 match checked_mul_pow_ten(coeff, shift as u8) {
77 None => {
78 Result::Err(ParseDecimalError::MaxValueExceeded)
79 }
80 Some(coeff) => Ok(Self::new_raw(coeff)),
81 }
82 }
83 Ordering::Greater => {
84 Result::Err(ParseDecimalError::PrecLimitExceeded)
85 }
86 }
87 }
88 }
89}
90
91impl<const P: u8> TryFrom<&str> for Decimal<P>
92where
93 PrecLimitCheck<{ P <= MAX_PREC }>: True,
94{
95 type Error = ParseDecimalError;
96
97 #[inline]
98 fn try_from(lit: &str) -> Result<Self, Self::Error> {
99 Self::from_str(lit)
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use std::{convert::TryFrom, str::FromStr};
106
107 use rust_fixed_point_decimal_core::ParseDecimalError;
108
109 use crate::Decimal;
110
111 #[test]
112 fn test_from_int_lit() {
113 let d = Decimal::<4>::from_str("1957945").unwrap();
114 assert_eq!(d.coeff, 19579450000);
115 }
116
117 #[test]
118 fn test_from_int_lit_no_prec() {
119 let d = Decimal::<0>::from_str("1957945").unwrap();
120 assert_eq!(d.coeff, 1957945);
121 }
122
123 #[test]
124 fn test_from_dec_lit() {
125 let d = Decimal::<2>::from_str("-17.5").unwrap();
126 assert_eq!(d.coeff, -1750);
127 }
128
129 #[test]
130 fn test_from_frac_only_lit() {
131 let d = Decimal::<7>::from_str("+.75").unwrap();
132 assert_eq!(d.coeff, 7500000);
133 }
134
135 #[test]
136 fn test_from_int_lit_neg_exp() {
137 let d = Decimal::<5>::from_str("17e-5").unwrap();
138 assert_eq!(d.coeff, 17);
139 }
140
141 #[test]
142 fn test_from_int_lit_pos_exp() {
143 let d = Decimal::<1>::from_str("+217e3").unwrap();
144 assert_eq!(d.coeff, 2170000);
145 }
146
147 #[test]
148 fn test_from_dec_lit_neg_exp() {
149 let d = Decimal::<3>::from_str("-533.7e-2").unwrap();
150 assert_eq!(d.coeff, -5337);
151 }
152
153 #[test]
154 fn test_from_dec_lit_pos_exp() {
155 let d = Decimal::<1>::from_str("700004.002E13").unwrap();
156 assert_eq!(d.coeff, 70000400200000000000);
157 }
158
159 #[test]
160 fn test_err_empty_str() {
161 let res = Decimal::<2>::from_str("");
162 assert!(res.is_err());
163 let err = res.unwrap_err();
164 assert_eq!(err, ParseDecimalError::Empty);
165 }
166
167 #[test]
168 fn test_err_invalid_lit() {
169 let lits = [" ", "+", "-4.33.2", "2.87 e3", "+e3", ".4e3 "];
170 for lit in lits {
171 let res = Decimal::<2>::from_str(lit);
172 assert!(res.is_err());
173 let err = res.unwrap_err();
174 assert_eq!(err, ParseDecimalError::Invalid);
175 }
176 }
177
178 #[test]
179 fn test_prec_limit_exceeded() {
180 let res = Decimal::<2>::from_str("17.295");
181 assert!(res.is_err());
182 let err = res.unwrap_err();
183 assert_eq!(err, ParseDecimalError::PrecLimitExceeded);
184 }
185
186 #[test]
187 fn test_prec_limit_exceeded_with_exp() {
188 let res = Decimal::<3>::from_str("17.4e-3");
189 assert!(res.is_err());
190 let err = res.unwrap_err();
191 assert_eq!(err, ParseDecimalError::PrecLimitExceeded);
192 }
193
194 #[test]
195 fn test_int_lit_max_val_dec0_exceeded() {
196 let i = i128::MIN;
197 let mut s = format!("{}", i);
198 s.remove(0);
199 let res = Decimal::<0>::from_str(&s);
200 assert!(res.is_err());
201 let err = res.unwrap_err();
202 assert_eq!(err, ParseDecimalError::MaxValueExceeded);
203 }
204
205 #[test]
206 fn test_int_lit_max_val_dec2_exceeded() {
207 let i = i128::MAX / 100 + 1;
208 let s = format!("{}", i);
209 let res = Decimal::<2>::from_str(&s);
210 assert!(res.is_err());
211 let err = res.unwrap_err();
212 assert_eq!(err, ParseDecimalError::MaxValueExceeded);
213 }
214
215 #[test]
216 fn test_dec_lit_max_val_exceeded() {
217 let s = "123456789012345678901234567890123.4567890";
218 let res = Decimal::<7>::from_str(&s);
219 assert!(res.is_err());
220 let err = res.unwrap_err();
221 assert_eq!(err, ParseDecimalError::MaxValueExceeded);
222 }
223
224 #[test]
225 fn test_parse() {
226 let s = "+28.700";
227 let res = s.parse::<Decimal<3>>();
228 assert!(!res.is_err());
229 let dec = res.unwrap();
230 assert_eq!(dec.coeff, 28700);
231 }
232
233 #[test]
234 fn test_parse_prec_limit_exceeded() {
235 let s = "+28.7005";
236 let res = s.parse::<Decimal<3>>();
237 assert!(res.is_err());
238 let err = res.unwrap_err();
239 assert_eq!(err, ParseDecimalError::PrecLimitExceeded);
240 }
241
242 #[test]
243 fn test_try_from() {
244 let s = "-534000.708";
245 let res = Decimal::<4>::try_from(s);
246 assert!(!res.is_err());
247 let dec = res.unwrap();
248 assert_eq!(dec.coeff, -5340007080);
249 }
250
251 #[test]
252 fn test_try_from_prec_limit_exceeded() {
253 let s = "+28.700500";
254 let res = Decimal::<5>::try_from(s);
255 assert!(res.is_err());
256 let err = res.unwrap_err();
257 assert_eq!(err, ParseDecimalError::PrecLimitExceeded);
258 }
259}