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