1use chrono::Datelike;
2
3use crate::{
4 locale::Locale,
5 number_format::{parse_finite_number, to_precision},
6};
7
8use super::{
9 dates::{date_to_serial_number, from_excel_date},
10 parser::{ParsePart, Parser, TextToken},
11};
12
13pub struct Formatted {
14 pub color: Option<i32>,
15 pub text: String,
16 pub error: Option<String>,
17}
18
19fn get_fract_part(value: f64, precision: i32, int_len: usize) -> Vec<char> {
22 let b = format!("{:.1$}", value.fract(), precision as usize)
23 .chars()
24 .collect::<Vec<char>>();
25 let l = b.len() - 1;
26 let mut last_non_zero = b.len() - 1;
27 for i in 0..l {
28 if b[l - i] != '0' {
29 last_non_zero = l - i + 1;
30 break;
31 }
32 }
33 if last_non_zero < 2 {
34 return vec![];
35 }
36 let max_len = if int_len > 15 {
37 2_usize
38 } else {
39 15_usize - int_len + 1
40 };
41 let last_non_zero = usize::min(last_non_zero, max_len + 1);
42 b[2..last_non_zero].to_vec()
43}
44
45fn use_group_separator(use_thousands: bool, digit_index: i32, group_sizes: &str) -> bool {
48 if use_thousands {
49 if group_sizes == "#,##0.###" {
50 if digit_index > 1 && (digit_index - 1) % 3 == 0 {
51 return true;
52 }
53 } else if group_sizes == "#,##,##0.###"
54 && (digit_index == 3 || (digit_index > 3 && digit_index % 2 == 0))
55 {
56 return true;
57 }
58 }
59 false
60}
61
62pub fn format_number(value_original: f64, format: &str, locale: &Locale) -> Formatted {
63 let mut parser = Parser::new(format);
64 parser.parse();
65 let parts = parser.parts;
66 let mut value = value_original;
78 let part;
79 match parts.len() {
80 1 => {
81 part = &parts[0];
82 }
83 2 => {
84 if value >= 0.0 {
85 part = &parts[0]
86 } else {
87 value = -value;
88 part = &parts[1];
89 }
90 }
91 3 => {
92 if value > 0.0 {
93 part = &parts[0]
94 } else if value < 0.0 {
95 value = -value;
96 part = &parts[1];
97 } else {
98 value = 0.0;
99 part = &parts[2];
100 }
101 }
102 4 => {
103 if value > 0.0 {
104 part = &parts[0]
105 } else if value < 0.0 {
106 value = -value;
107 part = &parts[1];
108 } else {
109 value = 0.0;
110 part = &parts[2];
111 }
112 }
113 _ => {
114 return Formatted {
115 text: "#VALUE!".to_owned(),
116 color: None,
117 error: Some("Too many parts".to_owned()),
118 };
119 }
120 }
121 match part {
122 ParsePart::Error(..) => Formatted {
123 text: "#VALUE!".to_owned(),
124 color: None,
125 error: Some("Problem parsing format string".to_owned()),
126 },
127 ParsePart::General(..) => {
128 let value_abs = value.abs();
131 if (1.0e-8..1.0e+11).contains(&value_abs) {
132 let mut text = format!("{value:.9}");
133 text = text.trim_end_matches('0').trim_end_matches('.').to_string();
134 if locale.numbers.symbols.decimal != "." {
135 text = text.replace('.', &locale.numbers.symbols.decimal.to_string());
136 }
137 Formatted {
138 text,
139 color: None,
140 error: None,
141 }
142 } else {
143 if value_abs == 0.0 {
144 return Formatted {
145 text: "0".to_string(),
146 color: None,
147 error: None,
148 };
149 }
150 let exponent = value_abs.log10().floor();
151 value /= 10.0_f64.powf(exponent);
152 let sign = if exponent < 0.0 { '-' } else { '+' };
153 let s = format!("{value:.5}");
154 let mut text = format!(
155 "{}E{}{:02}",
156 s.trim_end_matches('0').trim_end_matches('.'),
157 sign,
158 exponent.abs()
159 );
160 if locale.numbers.symbols.decimal != "." {
161 text = text.replace('.', &locale.numbers.symbols.decimal.to_string());
162 }
163 Formatted {
164 text,
165 color: None,
166 error: None,
167 }
168 }
169 }
170 ParsePart::Date(p) => {
171 let tokens = &p.tokens;
172 let mut text = "".to_string();
173 let time_fract = value.fract();
174 let hours = (time_fract * 24.0).floor();
175 let minutes = ((time_fract * 24.0 - hours) * 60.0).floor();
176 let seconds = ((((time_fract * 24.0 - hours) * 60.0) - minutes) * 60.0).round();
177 let date = from_excel_date(value as i64).ok();
178 for token in tokens {
179 match token {
180 TextToken::Literal(c) => {
181 text = format!("{text}{c}");
182 }
183 TextToken::Text(t) => {
184 text = format!("{text}{t}");
185 }
186 TextToken::Ghost(_) => {
187 text = format!("{text} ");
190 }
191 TextToken::Spacer(_) => {
192 text = format!("{text} ");
195 }
196 TextToken::Raw => {
197 text = format!("{text}{value}");
198 }
199 TextToken::Digit(_) => {}
200 TextToken::Period => {}
201 TextToken::Day => match date {
202 Some(date) => {
203 let day = date.day() as usize;
204 text = format!("{text}{day}");
205 }
206 None => {
207 return Formatted {
208 text: "#VALUE!".to_owned(),
209 color: None,
210 error: Some(format!("Invalid date value: '{value}'")),
211 }
212 }
213 },
214 TextToken::DayPadded => {
215 let date = match date {
216 Some(d) => d,
217 None => {
218 return Formatted {
219 text: "#VALUE!".to_owned(),
220 color: None,
221 error: Some(format!("Invalid date value: '{value}'")),
222 }
223 }
224 };
225 let day = date.day() as usize;
226 text = format!("{text}{day:02}");
227 }
228 TextToken::DayNameShort => {
229 let date = match date {
230 Some(d) => d,
231 None => {
232 return Formatted {
233 text: "#VALUE!".to_owned(),
234 color: None,
235 error: Some(format!("Invalid date value: '{value}'")),
236 }
237 }
238 };
239 let mut day = date.weekday().number_from_monday() as usize;
240 if day == 7 {
241 day = 0;
242 }
243 text = format!("{}{}", text, locale.dates.day_names_short[day]);
244 }
245 TextToken::DayName => {
246 let date = match date {
247 Some(d) => d,
248 None => {
249 return Formatted {
250 text: "#VALUE!".to_owned(),
251 color: None,
252 error: Some(format!("Invalid date value: '{value}'")),
253 }
254 }
255 };
256 let mut day = date.weekday().number_from_monday() as usize;
257 if day == 7 {
258 day = 0;
259 }
260 text = format!("{}{}", text, locale.dates.day_names[day]);
261 }
262 TextToken::Month => {
263 let date = match date {
264 Some(d) => d,
265 None => {
266 return Formatted {
267 text: "#VALUE!".to_owned(),
268 color: None,
269 error: Some(format!("Invalid date value: '{value}'")),
270 }
271 }
272 };
273 let month = date.month() as usize;
274 text = format!("{text}{month}");
275 }
276 TextToken::MonthPadded => {
277 let date = match date {
278 Some(d) => d,
279 None => {
280 return Formatted {
281 text: "#VALUE!".to_owned(),
282 color: None,
283 error: Some(format!("Invalid date value: '{value}'")),
284 }
285 }
286 };
287 let month = date.month() as usize;
288 text = format!("{text}{month:02}");
289 }
290 TextToken::MonthNameShort => {
291 let date = match date {
292 Some(d) => d,
293 None => {
294 return Formatted {
295 text: "#VALUE!".to_owned(),
296 color: None,
297 error: Some(format!("Invalid date value: '{value}'")),
298 }
299 }
300 };
301 let month = date.month() as usize;
302 text = format!("{}{}", text, locale.dates.months_short[month - 1]);
303 }
304 TextToken::MonthName => {
305 let date = match date {
306 Some(d) => d,
307 None => {
308 return Formatted {
309 text: "#VALUE!".to_owned(),
310 color: None,
311 error: Some(format!("Invalid date value: '{value}'")),
312 }
313 }
314 };
315 let month = date.month() as usize;
316 text = format!("{}{}", text, locale.dates.months[month - 1]);
317 }
318 TextToken::MonthLetter => {
319 let date = match date {
320 Some(d) => d,
321 None => {
322 return Formatted {
323 text: "#VALUE!".to_owned(),
324 color: None,
325 error: Some(format!("Invalid date value: '{value}'")),
326 }
327 }
328 };
329 let month = date.month() as usize;
330 let months_letter = &locale.dates.months_letter[month - 1];
331 text = format!("{text}{months_letter}");
332 }
333 TextToken::YearShort => {
334 let date = match date {
335 Some(d) => d,
336 None => {
337 return Formatted {
338 text: "#VALUE!".to_owned(),
339 color: None,
340 error: Some(format!("Invalid date value: '{value}'")),
341 }
342 }
343 };
344 text = format!("{}{}", text, date.format("%y"));
345 }
346 TextToken::Year => {
347 let date = match date {
348 Some(d) => d,
349 None => {
350 return Formatted {
351 text: "#VALUE!".to_owned(),
352 color: None,
353 error: Some(format!("Invalid date value: '{value}'")),
354 }
355 }
356 };
357 text = format!("{}{}", text, date.year());
358 }
359 TextToken::Hour => {
360 let mut hour = hours as i32;
361 if p.use_ampm {
362 if hour == 0 {
363 hour = 12;
364 } else if hour > 12 {
365 hour -= 12;
366 }
367 }
368 text = format!("{text}{hour}");
369 }
370 TextToken::HourPadded => {
371 let mut hour = hours as i32;
372 if p.use_ampm {
373 if hour == 0 {
374 hour = 12;
375 } else if hour > 12 {
376 hour -= 12;
377 }
378 }
379 text = format!("{text}{hour:02}");
380 }
381 TextToken::Second => {
382 let second = seconds as i32;
383 text = format!("{text}{second}");
384 }
385 TextToken::SecondPadded => {
386 let second = seconds as i32;
387 text = format!("{text}{second:02}");
388 }
389 TextToken::AMPM => {
390 let ampm = if hours < 12.0 { "AM" } else { "PM" };
391 text = format!("{text}{ampm}");
392 }
393 TextToken::Minute => {
394 let minute = minutes as i32;
395 text = format!("{text}{minute}");
396 }
397 TextToken::MinutePadded => {
398 let minute = minutes as i32;
399 text = format!("{text}{minute:02}");
400 }
401 TextToken::ElapsedHour => {
402 let hour = (value * 24.0).floor() as i32;
403 text = format!("{text}{hour}");
404 }
405 TextToken::ElapsedHourPadded => {
406 let hour = (value * 24.0).floor() as i32;
407 text = format!("{text}{hour:02}");
408 }
409 TextToken::ElapsedMinute => {
410 let minute = (value * 24.0 * 60.0).floor() as i32;
411 text = format!("{text}{minute}");
412 }
413 TextToken::ElapsedMinutePadded => {
414 let minute = (value * 24.0 * 60.0).floor() as i32;
415 text = format!("{text}{minute:02}");
416 }
417 TextToken::ElapsedSecond => {
418 let second = (value * 24.0 * 60.0 * 60.0).floor() as i32;
419 text = format!("{text}{second}");
420 }
421 TextToken::ElapsedSecondPadded => {
422 let second = (value * 24.0 * 60.0 * 60.0).floor() as i32;
423 text = format!("{text}{second:02}");
424 }
425 }
426 }
427 Formatted {
428 text,
429 color: p.color,
430 error: None,
431 }
432 }
433 ParsePart::Number(p) => {
434 let mut text = "".to_string();
435 if let Some(c) = p.currency {
436 text = format!("{c}");
437 }
438 let tokens = &p.tokens;
439 value = value * 100.0_f64.powi(p.percent) / (1000.0_f64.powi(p.comma));
440 value = to_precision(
442 value,
443 (p.precision as usize) + format!("{}", value.abs().floor()).len(),
444 );
445 let mut value_abs = value.abs();
446 let mut exponent_part: Vec<char> = vec![];
447 let mut exponent_is_negative = value_abs < 10.0;
448 if p.is_scientific {
449 if value_abs == 0.0 {
450 exponent_part = vec!['0'];
451 exponent_is_negative = false;
452 } else {
453 let exponent = value_abs.log10().floor();
455 exponent_part = format!("{}", exponent.abs()).chars().collect();
456 value /= 10.0_f64.powf(exponent);
457 value = to_precision(value, 15);
458 value_abs = value.abs();
459 }
460 }
461 let l_exp = exponent_part.len() as i32;
462 let int_number = if p.precision == 0 {
463 value_abs.round()
464 } else {
465 value_abs.floor()
466 };
467 let mut int_part: Vec<char> = format!("{}", int_number).chars().collect();
468 if int_number as i64 == 0 {
469 int_part = vec![];
470 }
471 let fract_part = get_fract_part(value_abs, p.precision, int_part.len());
472 let ln = int_part.len() as i32;
474 let digit_count = p.digit_count;
476 let mut digit_index = 0;
478
479 let symbols = &locale.numbers.symbols;
480 let group_sizes = locale.numbers.decimal_formats.standard.to_owned();
481 let group_separator = symbols.group.to_owned();
482 let decimal_separator = symbols.decimal.to_owned();
483 let is_negative = value < -(10.0_f64.powf(-(p.precision as f64)));
485 let mut needs_period = false;
486
487 for token in tokens {
488 match token {
489 TextToken::Literal(c) => {
490 text = format!("{text}{c}");
491 }
492 TextToken::Text(t) => {
493 text = format!("{text}{t}");
494 }
495 TextToken::Ghost(_) => {
496 text = format!("{text} ");
499 }
500 TextToken::Spacer(_) => {
501 text = format!("{text} ");
504 }
505 TextToken::Raw => {
506 text = format!("{text}{value}");
507 }
508 TextToken::Period => {
509 needs_period = true;
513 }
514 TextToken::Digit(digit) => {
515 if digit.number.is_integer() {
516 let index = digit.index;
518 let number_index = ln - digit_count + index;
519 if index == 0 && is_negative {
520 text = format!("-{text}");
521 }
522 if ln <= digit_count {
523 if !(number_index < 0 && digit.kind == '#') {
526 let c = if number_index < 0 {
527 if digit.kind == '0' {
528 '0'
529 } else {
530 ' '
532 }
533 } else {
534 int_part[number_index as usize]
535 };
536 let sep = if use_group_separator(
537 p.use_thousands,
538 ln - digit_index,
539 &group_sizes,
540 ) {
541 &group_separator
542 } else {
543 ""
544 };
545 text = format!("{text}{c}{sep}");
546 }
547 digit_index += 1;
548 } else {
549 for i in digit_index..number_index + 1 {
552 let sep = if use_group_separator(
553 p.use_thousands,
554 ln - i,
555 &group_sizes,
556 ) {
557 &group_separator
558 } else {
559 ""
560 };
561 text = format!("{}{}{}", text, int_part[i as usize], sep);
562 }
563 digit_index = number_index + 1;
564 }
565 } else if digit.number.is_decimal() {
566 let index = digit.index as usize;
568 if index < fract_part.len() {
569 if needs_period {
570 text = format!(
571 "{}{}{}",
572 text, decimal_separator, fract_part[index]
573 );
574 } else {
575 text = format!("{}{}", text, fract_part[index]);
576 }
577 } else if digit.kind == '0' {
578 if needs_period {
579 text = format!("{text}{}0", decimal_separator);
580 } else {
581 text = format!("{text}0");
582 }
583 } else if digit.kind == '?' {
584 text = format!("{text} ");
585 } else if digit.kind == '#' && needs_period {
586 text = format!("{text}{}", decimal_separator);
588 }
589 needs_period = false;
590 } else if digit.number.is_exponent() {
591 let index = digit.index;
593 if index == 0 {
594 if exponent_is_negative {
595 text = format!("{text}E-");
596 } else if p.scientific_minus {
597 text = format!("{text}E");
598 } else {
599 text = format!("{text}E+");
600 }
601 }
602 let number_index = l_exp - (p.exponent_digit_count - index);
603 if l_exp <= p.exponent_digit_count {
604 if !(number_index < 0 && digit.kind == '#') {
605 let c = if number_index < 0 {
606 if digit.kind == '?' {
607 ' '
608 } else {
609 '0'
610 }
611 } else {
612 exponent_part[number_index as usize]
613 };
614
615 text = format!("{text}{c}");
616 }
617 } else {
618 for i in 0..number_index + 1 {
619 text = format!("{}{}", text, exponent_part[i as usize]);
620 }
621 digit_index += number_index + 1;
622 }
623 }
624 }
625 TextToken::Day => {}
627 TextToken::DayPadded => {}
628 TextToken::DayNameShort => {}
629 TextToken::DayName => {}
630 TextToken::Month => {}
631 TextToken::MonthPadded => {}
632 TextToken::MonthNameShort => {}
633 TextToken::MonthName => {}
634 TextToken::MonthLetter => {}
635 TextToken::YearShort => {}
636 TextToken::Year => {}
637 TextToken::Hour => {}
638 TextToken::HourPadded => {}
639 TextToken::Minute => {}
640 TextToken::MinutePadded => {}
641 TextToken::Second => {}
642 TextToken::SecondPadded => {}
643 TextToken::AMPM => {}
644 TextToken::ElapsedHour => {}
645 TextToken::ElapsedHourPadded => {}
646 TextToken::ElapsedMinute => {}
647 TextToken::ElapsedMinutePadded => {}
648 TextToken::ElapsedSecond => {}
649 TextToken::ElapsedSecondPadded => {}
650 }
651 }
652 Formatted {
653 text,
654 color: p.color,
655 error: None,
656 }
657 }
658 }
659}
660
661fn parse_day(day_str: &str) -> Result<(u32, String), String> {
662 let bytes = day_str.bytes();
663 let bytes_len = bytes.len();
664 if bytes_len <= 2 {
665 match day_str.parse::<u32>() {
666 Ok(y) => {
667 if bytes_len == 2 {
668 return Ok((y, "dd".to_string()));
669 } else {
670 return Ok((y, "d".to_string()));
671 }
672 }
673 Err(_) => return Err("Not a valid day".to_string()),
674 }
675 }
676 Err("Not a valid day".to_string())
677}
678
679fn parse_month(month_str: &str, locale: &Locale) -> Result<(u32, String), String> {
680 let bytes = month_str.bytes();
681 let bytes_len = bytes.len();
682 if bytes_len <= 2 {
683 match month_str.parse::<u32>() {
684 Ok(y) => {
685 if bytes_len == 2 {
686 return Ok((y, "mm".to_string()));
687 } else {
688 return Ok((y, "m".to_string()));
689 }
690 }
691 Err(_) => return Err("Not a valid month".to_string()),
692 }
693 }
694
695 let month_names_short = &locale.dates.months_short;
696 let month_names_long = &locale.dates.months;
697 if let Some(m) = month_names_short.iter().position(|r| r == month_str) {
698 return Ok((m as u32 + 1, "mmm".to_string()));
699 }
700 if let Some(m) = month_names_long.iter().position(|r| r == month_str) {
701 return Ok((m as u32 + 1, "mmmm".to_string()));
702 }
703 Err("Not a valid month".to_string())
704}
705
706fn parse_year(year_str: &str) -> Result<(i32, String), String> {
707 let bytes = year_str.bytes();
714 let bytes_len = bytes.len();
715 if bytes_len != 2 && bytes_len != 4 {
716 return Err("Not a valid year".to_string());
717 }
718 match year_str.parse::<i32>() {
719 Ok(y) => {
720 if y < 30 {
721 Ok((2000 + y, "yy".to_string()))
722 } else if y < 100 {
723 Ok((1900 + y, "yy".to_string()))
724 } else {
725 Ok((y, "yyyy".to_string()))
726 }
727 }
728 Err(_) => Err("Not a valid year".to_string()),
729 }
730}
731
732pub(crate) fn parse_date(value: &str, locale: &Locale) -> Result<(i32, String), String> {
752 let separator = if value.contains('/') {
753 '/'
754 } else if value.contains('-') {
755 '-'
756 } else if value.contains('.') {
757 '.'
758 } else {
759 return Err("Not a valid date".to_string());
760 };
761
762 let parts: Vec<&str> = value.split(separator).collect();
763 let mut is_iso_date = false;
764 let mut day_first = true;
765 let (day_str, month_str, year_str) = if parts.len() == 3 {
766 if parts[0].len() == 4 {
767 if !parts[1].chars().all(char::is_numeric) {
769 return Err("Not a valid date".to_string());
770 }
771 if !parts[2].chars().all(char::is_numeric) {
772 return Err("Not a valid date".to_string());
773 }
774 is_iso_date = true;
775 (parts[2], parts[1], parts[0])
776 } else {
777 if locale.dates.date_formats.short.starts_with('d') {
780 (parts[0], parts[1], parts[2])
781 } else {
782 day_first = false;
783 (parts[1], parts[0], parts[2])
784 }
785 }
786 } else {
787 return Err("Not a valid date".to_string());
788 };
789 let (day, day_format) = parse_day(day_str)?;
790 let (month, month_format) = parse_month(month_str, locale)?;
791 let (year, year_format) = parse_year(year_str)?;
792 let serial_number = match date_to_serial_number(day, month, year) {
793 Ok(n) => n,
794 Err(_) => return Err("Not a valid date".to_string()),
795 };
796 if is_iso_date {
797 Ok((
798 serial_number,
799 format!("yyyy{separator}{month_format}{separator}{day_format}"),
800 ))
801 } else if !day_first {
802 Ok((
803 serial_number,
804 format!("{month_format}{separator}{day_format}{separator}{year_format}"),
805 ))
806 } else {
807 Ok((
808 serial_number,
809 format!("{day_format}{separator}{month_format}{separator}{year_format}"),
810 ))
811 }
812}
813
814pub(crate) fn parse_formatted_number(
820 original: &str,
821 currencies: &[&str],
822 locale: &Locale,
823) -> Result<(f64, Option<String>), String> {
824 let value = original.trim();
825 let scientific_format = "0.00E+00";
826
827 let symbols = &locale.numbers.symbols;
828 let decimal_separator = symbols.decimal.chars().next().unwrap_or('.');
829 let group_separator = symbols.group.chars().next().unwrap_or(',');
830
831 if let Some(p) = value.strip_suffix('%') {
833 let (f, options) = parse_number(p.trim(), decimal_separator, group_separator)?;
834 if options.is_scientific {
835 return Ok((f / 100.0, Some(scientific_format.to_string())));
836 }
837 if options.decimal_digits > 0 {
839 return Ok((f / 100.0, Some("#,##0.00%".to_string())));
841 }
842 return Ok((f / 100.0, Some("#,##0%".to_string())));
844 }
845
846 for currency in currencies {
848 if let Some(p) = value.strip_prefix(&format!("-{currency}")) {
849 let (f, options) = parse_number(p.trim(), decimal_separator, group_separator)?;
850 if options.is_scientific {
851 return Ok((f, Some(scientific_format.to_string())));
852 }
853 if options.decimal_digits > 0 {
854 return Ok((-f, Some(format!("{currency}#,##0.00"))));
855 }
856 return Ok((-f, Some(format!("{currency}#,##0"))));
857 } else if let Some(p) = value.strip_prefix(currency) {
858 let (f, options) = parse_number(p.trim(), decimal_separator, group_separator)?;
859 if options.is_scientific {
860 return Ok((f, Some(scientific_format.to_string())));
861 }
862 if options.decimal_digits > 0 {
863 return Ok((f, Some(format!("{currency}#,##0.00"))));
864 }
865 return Ok((f, Some(format!("{currency}#,##0"))));
866 } else if let Some(p) = value.strip_suffix(currency) {
867 let (f, options) = parse_number(p.trim(), decimal_separator, group_separator)?;
868 if options.is_scientific {
869 return Ok((f, Some(scientific_format.to_string())));
870 }
871 if options.decimal_digits > 0 {
872 let currency_format = &format!("#,##0.00{currency}");
873 return Ok((f, Some(currency_format.to_string())));
874 }
875 let currency_format = &format!("#,##0{currency}");
876 return Ok((f, Some(currency_format.to_string())));
877 }
878 }
879
880 if let Ok((serial_number, format)) = parse_date(original, locale) {
882 return Ok((serial_number as f64, Some(format)));
883 }
884
885 let (f, options) = parse_number(value, decimal_separator, group_separator)?;
887 if options.is_scientific {
888 return Ok((f, Some(scientific_format.to_string())));
889 }
890 if options.has_commas {
891 if options.decimal_digits > 0 {
892 return Ok((f, Some("#,##0.00".to_string())));
894 }
895 return Ok((f, Some("#,##0".to_string())));
897 }
898 Ok((f, None))
899}
900
901struct NumberOptions {
902 has_commas: bool,
903 is_scientific: bool,
904 decimal_digits: usize,
905}
906
907fn parse_number(
910 value: &str,
911 decimal_separator: char,
912 group_separator: char,
913) -> Result<(f64, NumberOptions), String> {
914 let mut position = 0;
915 let characters: Vec<char> = value.chars().collect();
916 let len = characters.len();
917 if len == 0 {
918 return Err("Cannot parse number".to_string());
919 }
920 let mut chars = String::from("");
921 let mut group_separator_index = Vec::new();
922 let sign = if characters[0] == '-' {
924 position += 1;
925 -1.0
926 } else if characters[0] == '+' {
927 position += 1;
928 1.0
929 } else {
930 1.0
931 };
932
933 if position >= len {
934 return Err("Cannot parse number".to_string());
935 }
936
937 if characters[position] == group_separator {
938 return Err("Cannot parse number".to_string());
939 }
940
941 while position < len {
943 let x = characters[position];
944 if x.is_ascii_digit() {
945 chars.push(x);
946 } else if x == group_separator {
947 group_separator_index.push(chars.len());
948 } else {
949 break;
950 }
951 position += 1;
952 }
953 for index in &group_separator_index {
955 if (chars.len() - index) % 3 != 0 {
956 return Err("Cannot parse number".to_string());
957 }
958 }
959 let mut decimal_digits = 0;
960 if position < len && characters[position] == decimal_separator {
961 chars.push('.');
963 position += 1;
964 let start_position = 0;
965 while position < len {
966 let x = characters[position];
967 if x.is_ascii_digit() {
968 chars.push(x);
969 } else {
970 break;
971 }
972 position += 1;
973 }
974 decimal_digits = position - start_position;
975 }
976 let mut is_scientific = false;
977 if position + 1 < len && (characters[position] == 'e' || characters[position] == 'E') {
978 is_scientific = true;
980 let x = characters[position + 1];
981 if x == '-' || x == '+' || x.is_ascii_digit() {
982 chars.push('e');
983 chars.push(x);
984 position += 2;
985 while position < len {
986 let x = characters[position];
987 if x.is_ascii_digit() {
988 chars.push(x);
989 } else {
990 break;
991 }
992 position += 1;
993 }
994 }
995 }
996 if position != len {
997 return Err("Could not parse number".to_string());
998 };
999 match parse_finite_number(&chars) {
1000 None => Err("Failed to parse to double".to_string()),
1001 Some(v) => Ok((
1002 sign * v,
1003 NumberOptions {
1004 has_commas: !group_separator_index.is_empty(),
1005 is_scientific,
1006 decimal_digits,
1007 },
1008 )),
1009 }
1010}