rust_fixed_point_decimal_core/
parser.rs1use std::fmt::{Display, Formatter};
11
12use crate::powers_of_ten::checked_mul_pow_ten;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
19pub enum ParseDecimalError {
20 Empty,
22 Invalid,
24 PrecLimitExceeded,
27 MaxValueExceeded,
30}
31
32impl ParseDecimalError {
33 #[doc(hidden)]
34 pub fn _description(&self) -> &str {
35 match self {
36 ParseDecimalError::Empty => "Empty string.",
37 ParseDecimalError::Invalid => "Invalid decimal string literal.",
38 ParseDecimalError::PrecLimitExceeded => {
39 "Too many fractional digits."
40 }
41 ParseDecimalError::MaxValueExceeded => {
42 "Maximum representable value exceeded."
43 }
44 }
45 }
46}
47
48impl Display for ParseDecimalError {
49 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
50 Display::fmt(self._description(), f)
51 }
52}
53
54impl std::error::Error for ParseDecimalError {}
55
56struct DecLitParts<'a> {
57 num_sign: &'a str,
58 int_part: &'a str,
59 frac_part: &'a str,
60 exp_sign: &'a str,
61 exp_part: &'a str,
62}
63
64fn parse_decimal_literal(lit: &str) -> Result<DecLitParts, ParseDecimalError> {
68 let mut num_sign_range = 0usize..0usize;
69 let mut int_part_range = 0usize..0usize;
70 let mut frac_part_range = 0usize..0usize;
71 let mut exp_sign_range = 0usize..0usize;
72 let mut exp_part_range = 0usize..0usize;
73 let mut chars = lit.char_indices();
74 let mut next = chars.next();
75 if let None = next {
76 return Result::Err(ParseDecimalError::Empty);
77 }
78 let (mut curr_idx, mut curr_char) = next.unwrap();
79 if curr_char == '-' || curr_char == '+' {
80 num_sign_range = curr_idx..curr_idx + 1;
81 next = chars.next();
82 }
83 int_part_range.start = num_sign_range.end;
84 loop {
85 match next {
86 None => {
87 curr_idx = lit.len();
88 if int_part_range.start < curr_idx {
89 int_part_range.end = lit.len();
90 return Ok(DecLitParts {
91 num_sign: &lit[num_sign_range],
92 int_part: &lit[int_part_range],
93 frac_part: "",
94 exp_sign: "",
95 exp_part: "",
96 });
97 } else {
98 return Result::Err(ParseDecimalError::Invalid);
99 }
100 }
101 Some((idx, ch)) => {
102 if !ch.is_digit(10) {
103 int_part_range.end = idx;
104 curr_char = ch;
105 curr_idx = idx;
106 break;
107 }
108 }
109 }
110 next = chars.next();
111 }
112 if curr_char == '.' {
113 frac_part_range.start = curr_idx + 1;
114 next = chars.next();
115 loop {
116 match next {
117 None => {
118 frac_part_range.end = lit.len();
119 return Ok(DecLitParts {
120 num_sign: &lit[num_sign_range],
121 int_part: &lit[int_part_range],
122 frac_part: &lit[frac_part_range],
123 exp_sign: "",
124 exp_part: "",
125 });
126 }
127 Some((idx, ch)) => {
128 if !ch.is_digit(10) {
129 frac_part_range.end = idx;
130 curr_char = ch;
131 break;
132 }
133 }
134 }
135 next = chars.next();
136 }
137 }
138 if curr_char == 'e' || curr_char == 'E' {
139 next = chars.next();
140 if let None = next {
141 return Result::Err(ParseDecimalError::Invalid);
142 }
143 let (curr_idx, curr_char) = next.unwrap();
144 if curr_char == '-' || curr_char == '+' {
145 exp_sign_range = curr_idx..curr_idx + 1;
146 exp_part_range.start = curr_idx + 1;
147 } else if curr_char.is_digit(10) {
148 exp_part_range.start = curr_idx;
149 } else {
150 return Result::Err(ParseDecimalError::Invalid);
151 }
152 next = chars.next();
153 loop {
154 match next {
155 None => {
156 exp_part_range.end = lit.len();
157 break;
158 }
159 Some((_, ch)) => {
160 if !ch.is_digit(10) {
161 return Result::Err(ParseDecimalError::Invalid);
162 }
163 }
164 }
165 next = chars.next();
166 }
167 } else {
168 return Result::Err(ParseDecimalError::Invalid);
169 }
170 if int_part_range.len() == 0 && frac_part_range.len() == 0 {
171 return Result::Err(ParseDecimalError::Invalid);
172 }
173 Ok(DecLitParts {
174 num_sign: &lit[num_sign_range],
175 int_part: &lit[int_part_range],
176 frac_part: &lit[frac_part_range],
177 exp_sign: &lit[exp_sign_range],
178 exp_part: &lit[exp_part_range],
179 })
180}
181
182#[doc(hidden)]
189pub fn dec_repr_from_str(
190 lit: &str,
191) -> Result<(i128, isize), ParseDecimalError> {
192 let max_prec = crate::MAX_PREC as isize;
193 let parts = parse_decimal_literal(lit)?;
194 let exp: isize = if parts.exp_part.len() > 0 {
195 if parts.exp_sign == "-" {
196 -parts.exp_part.parse::<isize>().unwrap()
197 } else {
198 parts.exp_part.parse().unwrap()
199 }
200 } else {
201 0
202 };
203 let n_frac_digits = parts.frac_part.len() as isize;
204 if n_frac_digits - exp > max_prec {
205 return Result::Err(ParseDecimalError::PrecLimitExceeded);
206 }
207 let mut coeff: i128 = if parts.int_part.len() > 0 {
208 match parts.int_part.parse() {
209 Err(_) => {
210 return Err(ParseDecimalError::MaxValueExceeded);
211 }
212 Ok(i) => i,
213 }
214 } else {
215 0
216 };
217 if n_frac_digits > 0 {
218 match checked_mul_pow_ten(coeff, n_frac_digits as u8) {
219 None => return Result::Err(ParseDecimalError::MaxValueExceeded),
220 Some(val) => coeff = val,
221 }
222 coeff += parts.frac_part.parse::<i128>().unwrap();
223 }
224 if parts.num_sign == "-" {
225 Ok((-coeff, exp - n_frac_digits))
226 } else {
227 Ok((coeff, exp - n_frac_digits))
228 }
229}