decstr/
text.rs

1/*!
2A text-based format for decimal numbers.
3
4This module implements text parsers for decimal numbers. The parsers take a number such as
5`-123.456e-789` and parse its features into a temporary buffer along with offsets for the
6decimal point and exponent. When the input number is a string then no temporary buffering
7needs to take place.
8
9The output from the text parser is a `ParsedDecimal` that classifies the number and gives offsets
10to any digits or other features within it. These offsets can be used to convert the number into
11different representations.
12
13This module is organized around _kinds_ of numbers.
14*/
15
16mod buf;
17mod finite;
18mod infinity;
19mod nan;
20
21pub use self::{
22    buf::*,
23    finite::*,
24    infinity::*,
25    nan::*,
26};
27
28use core::{
29    fmt::{
30        self,
31        Write,
32    },
33    ops::Range,
34    str,
35};
36
37use crate::ParseError;
38
39/**
40A decimal number parsed from its textual representation.
41*/
42#[derive(Debug, Clone, Eq, PartialEq)]
43pub enum ParsedDecimal<B> {
44    /**
45    The number is finite, like `-123.456e7`.
46    */
47    Finite(ParsedFinite<B>),
48    /**
49    The number is infinity, like `-inf`.
50    */
51    Infinity(ParsedInfinity),
52    /**
53    The number is a NaN, like `nan`.
54    */
55    Nan(ParsedNan<B>),
56}
57
58/**
59A decimal number parsed from a formatted string.
60
61The number contains a buffer of digits and some metadata about the significand and exponent.
62The buffer may be the original string parsed, or it could be digits buffered from it.
63 */
64#[derive(Debug, Clone, Eq, PartialEq)]
65pub struct ParsedFinite<B> {
66    pub finite_buf: B,
67    pub finite_significand: ParsedSignificand,
68    pub finite_exponent: Option<ParsedExponent>,
69}
70
71/**
72A value that is not a number parsed form a formatted string.
73
74The NaN may contain a payload, which is an integer value.
75*/
76#[derive(Debug, Clone, Eq, PartialEq)]
77pub struct ParsedNan<B> {
78    pub nan_buf: B,
79    pub nan_header: ParsedNanHeader,
80    pub nan_payload: Option<ParsedSignificand>,
81}
82
83/**
84An infinite value parsed form a formatted string.
85*/
86#[derive(Debug, Clone, Eq, PartialEq)]
87pub struct ParsedInfinity {
88    pub is_infinity_negative: bool,
89}
90
91impl Default for ParsedInfinity {
92    fn default() -> Self {
93        ParsedInfinity {
94            is_infinity_negative: false,
95        }
96    }
97}
98
99/**
100Offsets and metadata about the significand parsed from a formatted decimal
101 */
102#[derive(Debug, Clone, Eq, PartialEq)]
103pub struct ParsedSignificand {
104    pub significand_is_negative: bool,
105    pub significand_range: Range<usize>,
106    pub decimal_point: Option<ParsedDecimalPoint>,
107}
108
109impl Default for ParsedSignificand {
110    fn default() -> Self {
111        ParsedSignificand {
112            significand_is_negative: false,
113            significand_range: 0..0,
114            decimal_point: None,
115        }
116    }
117}
118
119/**
120Offsets and metadata about the significand decimal point parsed from a formatted decimal.
121 */
122#[derive(Debug, Clone, Eq, PartialEq)]
123pub struct ParsedDecimalPoint {
124    pub decimal_point_range: Range<usize>,
125}
126
127impl Default for ParsedDecimalPoint {
128    fn default() -> Self {
129        ParsedDecimalPoint {
130            decimal_point_range: 0..0,
131        }
132    }
133}
134
135/**
136Offsets and metadata about the exponent parsed from a formatted decimal.
137 */
138#[derive(Debug, Clone, Eq, PartialEq)]
139pub struct ParsedExponent {
140    pub exponent_is_negative: bool,
141    pub exponent_range: Range<usize>,
142}
143
144impl Default for ParsedExponent {
145    fn default() -> Self {
146        ParsedExponent {
147            exponent_is_negative: false,
148            exponent_range: 0..0,
149        }
150    }
151}
152
153/**
154The header for a NaN value.
155
156The header contains information about the NaN outside of its payload.
157*/
158#[derive(Debug, Clone, Eq, PartialEq)]
159pub struct ParsedNanHeader {
160    pub is_nan_signaling: bool,
161    pub is_nan_negative: bool,
162}
163
164impl Default for ParsedNanHeader {
165    fn default() -> Self {
166        ParsedNanHeader {
167            is_nan_signaling: false,
168            is_nan_negative: false,
169        }
170    }
171}
172
173/**
174A parser for a decimal number that may be finite, infinite, or NaN (not-a-number).
175*/
176#[derive(Debug)]
177pub struct DecimalParser<B>(DecimalParserInner<B>);
178
179#[derive(Debug)]
180enum DecimalParserInner<B> {
181    AtStart {
182        buf: Option<B>,
183        is_negative: Option<bool>,
184        error: Option<ParseError>,
185    },
186    Finite(FiniteParser<B>),
187    Infinity(InfinityParser<B>),
188    Nan(NanParser<B>),
189}
190
191impl<'a> DecimalParser<StrTextBuf<'a>> {
192    pub fn parse_str(input: &'a str) -> Result<ParsedDecimal<StrTextBuf<'a>>, ParseError> {
193        let mut parser = DecimalParser::begin(StrTextBuf::new(input));
194
195        parser.parse_ascii(input.as_bytes())?;
196
197        parser.end()
198    }
199}
200
201impl<B: TextWriter> DecimalParser<B> {
202    pub fn begin(buf: B) -> Self {
203        DecimalParser(DecimalParserInner::AtStart {
204            buf: Some(buf),
205            error: None,
206            is_negative: None,
207        })
208    }
209
210    pub fn parse_fmt(&mut self, f: impl fmt::Display) -> Result<(), ParseError> {
211        write!(self, "{}", f).map_err(|err| self.unwrap_context(err))
212    }
213
214    pub fn parse_ascii(&mut self, mut ascii: &[u8]) -> Result<(), ParseError> {
215        while !ascii.is_empty() {
216            match self.0 {
217                // If we're parsing a finite number then forward the remaining input to it
218                DecimalParserInner::Finite(ref mut finite) => return finite.parse_ascii(ascii),
219                // If we're at the start of the input then look for the first character that
220                // will tell us what kind of number we're expecting.
221                DecimalParserInner::AtStart {
222                    ref mut is_negative,
223                    ref mut buf,
224                    ..
225                } => match ascii[0] {
226                    // Finite
227                    b'0'..=b'9' => {
228                        let mut finite = FiniteParser::begin(buf.take().expect("missing buffer"));
229
230                        match is_negative {
231                            Some(false) => finite.significand_is_positive(),
232                            Some(true) => finite.significand_is_negative(),
233                            _ => (),
234                        }
235
236                        finite.push_significand_digit(ascii[0]);
237
238                        self.0 = DecimalParserInner::Finite(finite);
239                    }
240                    // A `-` sign doesn't tell us whether the number is finite or not
241                    // We stash it away until we know for sure
242                    b'-' if is_negative.is_none() => *is_negative = Some(true),
243                    // A `+` sign is treated the same as `-`
244                    b'+' if is_negative.is_none() => *is_negative = Some(false),
245                    // Signaling NaN
246                    b's' | b'S' => {
247                        let mut nan = NanParser::begin(buf.take().expect("missing buffer"));
248
249                        match is_negative {
250                            Some(false) => nan.nan_is_positive(b'+'),
251                            Some(true) => nan.nan_is_negative(b'-'),
252                            _ => (),
253                        }
254
255                        nan.nan_is_signaling(ascii[0]);
256
257                        self.0 = DecimalParserInner::Nan(nan);
258                    }
259                    // Quiet NaN
260                    b'n' | b'N' => {
261                        let mut nan = NanParser::begin(buf.take().expect("missing buffer"));
262
263                        match is_negative {
264                            Some(false) => nan.nan_is_positive(b'+'),
265                            Some(true) => nan.nan_is_negative(b'-'),
266                            _ => (),
267                        }
268
269                        nan.nan_is_quiet(ascii[0]);
270
271                        self.0 = DecimalParserInner::Nan(nan);
272                    }
273                    // Infinity
274                    b'i' | b'I' => {
275                        let mut inf = InfinityParser::begin(buf.take().expect("missing buffer"));
276
277                        match is_negative {
278                            Some(false) => inf.infinity_is_positive(),
279                            Some(true) => inf.infinity_is_negative(),
280                            _ => (),
281                        }
282
283                        inf.advance(ascii[0]);
284
285                        self.0 = DecimalParserInner::Infinity(inf)
286                    }
287                    // Any other character is invalid
288                    c => {
289                        return Err(ParseError::unexpected_char(
290                            c,
291                            "a finite number, infinity, or NaN",
292                        ))
293                    }
294                },
295                // If we're parsing infinity then forward the rest of the input to it
296                DecimalParserInner::Infinity(ref mut infinity) => {
297                    return infinity.parse_ascii(ascii);
298                }
299                // If we're parsing NaN then forward the rest of the input to it
300                DecimalParserInner::Nan(ref mut nan) => {
301                    return nan.parse_ascii(ascii);
302                }
303            }
304
305            ascii = &ascii[1..];
306        }
307
308        Ok(())
309    }
310
311    pub fn end(self) -> Result<ParsedDecimal<B>, ParseError> {
312        match self.0 {
313            DecimalParserInner::Finite(finite) => Ok(ParsedDecimal::Finite(finite.end()?)),
314            DecimalParserInner::Infinity(infinity) => Ok(ParsedDecimal::Infinity(infinity.end()?)),
315            DecimalParserInner::Nan(nan) => Ok(ParsedDecimal::Nan(nan.end()?)),
316            DecimalParserInner::AtStart { .. } => Err(ParseError::unexpected_end(
317                "a finite number, infinity, or NaN",
318            )),
319        }
320    }
321
322    pub fn context(&mut self, err: ParseError) -> fmt::Error {
323        match self.0 {
324            DecimalParserInner::AtStart { ref mut error, .. } => {
325                *error = Some(err);
326
327                fmt::Error
328            }
329            DecimalParserInner::Finite(ref mut parser) => parser.context(err),
330            DecimalParserInner::Infinity(ref mut parser) => parser.context(err),
331            DecimalParserInner::Nan(ref mut parser) => parser.context(err),
332        }
333    }
334
335    pub fn unwrap_context(&mut self, err: fmt::Error) -> ParseError {
336        match self.0 {
337            DecimalParserInner::AtStart { ref mut error, .. } => {
338                error.take().unwrap_or_else(ParseError::source)
339            }
340            DecimalParserInner::Finite(ref mut parser) => parser.unwrap_context(err),
341            DecimalParserInner::Infinity(ref mut parser) => parser.unwrap_context(err),
342            DecimalParserInner::Nan(ref mut parser) => parser.unwrap_context(err),
343        }
344    }
345}
346
347impl<B: TextWriter> Write for DecimalParser<B> {
348    fn write_str(&mut self, s: &str) -> fmt::Result {
349        self.parse_ascii(s.as_bytes())
350            .map_err(|err| self.context(err))
351    }
352}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357
358    #[test]
359    fn parse_decimal_propagates_input_to_sub_parsers() {
360        for (input, expected) in &[
361            (
362                "inf",
363                ParsedDecimal::<StrTextBuf>::Infinity(ParsedInfinity {
364                    is_infinity_negative: false,
365                }),
366            ),
367            (
368                "-inf",
369                ParsedDecimal::<StrTextBuf>::Infinity(ParsedInfinity {
370                    is_infinity_negative: true,
371                }),
372            ),
373            (
374                "NaN",
375                ParsedDecimal::<StrTextBuf>::Nan(ParsedNan {
376                    nan_buf: StrTextBuf::at_end("NaN"),
377                    nan_header: ParsedNanHeader {
378                        is_nan_signaling: false,
379                        is_nan_negative: false,
380                    },
381                    nan_payload: None,
382                }),
383            ),
384            (
385                "-NaN",
386                ParsedDecimal::<StrTextBuf>::Nan(ParsedNan {
387                    nan_buf: StrTextBuf::at_end("-NaN"),
388                    nan_header: ParsedNanHeader {
389                        is_nan_signaling: false,
390                        is_nan_negative: true,
391                    },
392                    nan_payload: None,
393                }),
394            ),
395            (
396                "+NaN",
397                ParsedDecimal::<StrTextBuf>::Nan(ParsedNan {
398                    nan_buf: StrTextBuf::at_end("+NaN"),
399                    nan_header: ParsedNanHeader {
400                        is_nan_signaling: false,
401                        is_nan_negative: false,
402                    },
403                    nan_payload: None,
404                }),
405            ),
406            (
407                "sNaN",
408                ParsedDecimal::<StrTextBuf>::Nan(ParsedNan {
409                    nan_buf: StrTextBuf::at_end("sNaN"),
410                    nan_header: ParsedNanHeader {
411                        is_nan_signaling: true,
412                        is_nan_negative: false,
413                    },
414                    nan_payload: None,
415                }),
416            ),
417            (
418                "-sNaN",
419                ParsedDecimal::<StrTextBuf>::Nan(ParsedNan {
420                    nan_buf: StrTextBuf::at_end("-sNaN"),
421                    nan_header: ParsedNanHeader {
422                        is_nan_signaling: true,
423                        is_nan_negative: true,
424                    },
425                    nan_payload: None,
426                }),
427            ),
428            (
429                "+sNaN",
430                ParsedDecimal::<StrTextBuf>::Nan(ParsedNan {
431                    nan_buf: StrTextBuf::at_end("+sNaN"),
432                    nan_header: ParsedNanHeader {
433                        is_nan_signaling: true,
434                        is_nan_negative: false,
435                    },
436                    nan_payload: None,
437                }),
438            ),
439            (
440                "1.23456e7",
441                ParsedDecimal::<StrTextBuf>::Finite(ParsedFinite {
442                    finite_buf: StrTextBuf::at_end("1.23456e7"),
443                    finite_significand: ParsedSignificand {
444                        significand_range: 0..7,
445                        decimal_point: Some(ParsedDecimalPoint {
446                            decimal_point_range: 1..2,
447                        }),
448                        ..Default::default()
449                    },
450                    finite_exponent: Some(ParsedExponent {
451                        exponent_range: 8..9,
452                        ..Default::default()
453                    }),
454                }),
455            ),
456            (
457                "-1.23456e7",
458                ParsedDecimal::<StrTextBuf>::Finite(ParsedFinite {
459                    finite_buf: StrTextBuf::at_end("-1.23456e7"),
460                    finite_significand: ParsedSignificand {
461                        significand_is_negative: true,
462                        significand_range: 1..8,
463                        decimal_point: Some(ParsedDecimalPoint {
464                            decimal_point_range: 2..3,
465                        }),
466                    },
467                    finite_exponent: Some(ParsedExponent {
468                        exponent_is_negative: false,
469                        exponent_range: 9..10,
470                    }),
471                }),
472            ),
473        ] {
474            let mut parser = DecimalParser::begin(StrTextBuf::new(input));
475            parser.write_str(input).expect("failed to parse");
476            let parsed = parser.end().expect("failed to parse");
477
478            assert_eq!(expected, &parsed, "{}", input);
479        }
480    }
481
482    #[test]
483    fn parse_finite_valid() {
484        for (input, expected) in &[
485            (
486                "0",
487                ParsedFinite {
488                    finite_buf: StrTextBuf::at_end("0"),
489                    finite_significand: ParsedSignificand {
490                        significand_is_negative: false,
491                        significand_range: 0..1,
492                        decimal_point: None,
493                    },
494                    finite_exponent: None,
495                },
496            ),
497            (
498                "-0",
499                ParsedFinite {
500                    finite_buf: StrTextBuf::at_end("-0"),
501                    finite_significand: ParsedSignificand {
502                        significand_is_negative: true,
503                        significand_range: 1..2,
504                        decimal_point: None,
505                    },
506                    finite_exponent: None,
507                },
508            ),
509            (
510                "+0",
511                ParsedFinite {
512                    finite_buf: StrTextBuf::at_end("+0"),
513                    finite_significand: ParsedSignificand {
514                        significand_is_negative: false,
515                        significand_range: 1..2,
516                        decimal_point: None,
517                    },
518                    finite_exponent: None,
519                },
520            ),
521            (
522                "00",
523                ParsedFinite {
524                    finite_buf: StrTextBuf::at_end("00"),
525                    finite_significand: ParsedSignificand {
526                        significand_is_negative: false,
527                        significand_range: 0..2,
528                        decimal_point: None,
529                    },
530                    finite_exponent: None,
531                },
532            ),
533            (
534                "0.0",
535                ParsedFinite {
536                    finite_buf: StrTextBuf::at_end("0.0"),
537                    finite_significand: ParsedSignificand {
538                        significand_is_negative: false,
539                        significand_range: 0..3,
540                        decimal_point: Some(ParsedDecimalPoint {
541                            decimal_point_range: 1..2,
542                        }),
543                    },
544                    finite_exponent: None,
545                },
546            ),
547            (
548                "0.00",
549                ParsedFinite {
550                    finite_buf: StrTextBuf::at_end("0.00"),
551                    finite_significand: ParsedSignificand {
552                        significand_is_negative: false,
553                        significand_range: 0..4,
554                        decimal_point: Some(ParsedDecimalPoint {
555                            decimal_point_range: 1..2,
556                        }),
557                    },
558                    finite_exponent: None,
559                },
560            ),
561            (
562                "123",
563                ParsedFinite {
564                    finite_buf: StrTextBuf::at_end("123"),
565                    finite_significand: ParsedSignificand {
566                        significand_is_negative: false,
567                        significand_range: 0..3,
568                        decimal_point: None,
569                    },
570                    finite_exponent: None,
571                },
572            ),
573            (
574                "123.456",
575                ParsedFinite {
576                    finite_buf: StrTextBuf::at_end("123.456"),
577                    finite_significand: ParsedSignificand {
578                        significand_is_negative: false,
579                        significand_range: 0..7,
580                        decimal_point: Some(ParsedDecimalPoint {
581                            decimal_point_range: 3..4,
582                        }),
583                    },
584                    finite_exponent: None,
585                },
586            ),
587            (
588                "123e456",
589                ParsedFinite {
590                    finite_buf: StrTextBuf::at_end("123e456"),
591                    finite_significand: ParsedSignificand {
592                        significand_is_negative: false,
593                        significand_range: 0..3,
594                        decimal_point: None,
595                    },
596                    finite_exponent: Some(ParsedExponent {
597                        exponent_is_negative: false,
598                        exponent_range: 4..7,
599                    }),
600                },
601            ),
602            (
603                "123.456e789",
604                ParsedFinite {
605                    finite_buf: StrTextBuf::at_end("123.456e789"),
606                    finite_significand: ParsedSignificand {
607                        significand_is_negative: false,
608                        significand_range: 0..7,
609                        decimal_point: Some(ParsedDecimalPoint {
610                            decimal_point_range: 3..4,
611                        }),
612                    },
613                    finite_exponent: Some(ParsedExponent {
614                        exponent_is_negative: false,
615                        exponent_range: 8..11,
616                    }),
617                },
618            ),
619            (
620                "123e-456",
621                ParsedFinite {
622                    finite_buf: StrTextBuf::at_end("123e-456"),
623                    finite_significand: ParsedSignificand {
624                        significand_is_negative: false,
625                        significand_range: 0..3,
626                        decimal_point: None,
627                    },
628                    finite_exponent: Some(ParsedExponent {
629                        exponent_is_negative: true,
630                        exponent_range: 5..8,
631                    }),
632                },
633            ),
634            (
635                "123.456e-789",
636                ParsedFinite {
637                    finite_buf: StrTextBuf::at_end("123.456e-789"),
638                    finite_significand: ParsedSignificand {
639                        significand_is_negative: false,
640                        significand_range: 0..7,
641                        decimal_point: Some(ParsedDecimalPoint {
642                            decimal_point_range: 3..4,
643                        }),
644                    },
645                    finite_exponent: Some(ParsedExponent {
646                        exponent_is_negative: true,
647                        exponent_range: 9..12,
648                    }),
649                },
650            ),
651        ] {
652            let mut parser = FiniteParser::begin(StrTextBuf::new(input));
653            parser.write_str(input).expect("failed to parse");
654            let parsed = parser.end().expect("failed to parse");
655
656            assert_eq!(expected, &parsed, "{}", input);
657        }
658    }
659
660    #[test]
661    fn parse_inf_valid() {
662        for (input, expected) in &[
663            (
664                "inf",
665                ParsedInfinity {
666                    is_infinity_negative: false,
667                },
668            ),
669            (
670                "-inf",
671                ParsedInfinity {
672                    is_infinity_negative: true,
673                },
674            ),
675            (
676                "+inf",
677                ParsedInfinity {
678                    is_infinity_negative: false,
679                },
680            ),
681            (
682                "infinity",
683                ParsedInfinity {
684                    is_infinity_negative: false,
685                },
686            ),
687            (
688                "-infinity",
689                ParsedInfinity {
690                    is_infinity_negative: true,
691                },
692            ),
693            (
694                "+infinity",
695                ParsedInfinity {
696                    is_infinity_negative: false,
697                },
698            ),
699            (
700                "INF",
701                ParsedInfinity {
702                    is_infinity_negative: false,
703                },
704            ),
705            (
706                "-INf",
707                ParsedInfinity {
708                    is_infinity_negative: true,
709                },
710            ),
711            (
712                "+INf",
713                ParsedInfinity {
714                    is_infinity_negative: false,
715                },
716            ),
717            (
718                "InFinITY",
719                ParsedInfinity {
720                    is_infinity_negative: false,
721                },
722            ),
723        ] {
724            let mut parser = InfinityParser::begin(StrTextBuf::new(input));
725            parser.write_str(input).expect("failed to parse");
726            let parsed = parser.end().expect("failed to parse");
727
728            assert_eq!(expected, &parsed, "{}", input);
729        }
730    }
731
732    #[test]
733    fn parse_nan_valid() {
734        for (input, expected) in &[
735            (
736                "nan",
737                ParsedNan {
738                    nan_buf: StrTextBuf::at_end("nan"),
739                    nan_header: ParsedNanHeader {
740                        is_nan_signaling: false,
741                        is_nan_negative: false,
742                    },
743                    nan_payload: None,
744                },
745            ),
746            (
747                "-nan",
748                ParsedNan {
749                    nan_buf: StrTextBuf::at_end("-nan"),
750                    nan_header: ParsedNanHeader {
751                        is_nan_signaling: false,
752                        is_nan_negative: true,
753                    },
754                    nan_payload: None,
755                },
756            ),
757            (
758                "+nan",
759                ParsedNan {
760                    nan_buf: StrTextBuf::at_end("+nan"),
761                    nan_header: ParsedNanHeader {
762                        is_nan_signaling: false,
763                        is_nan_negative: false,
764                    },
765                    nan_payload: None,
766                },
767            ),
768            (
769                "snan",
770                ParsedNan {
771                    nan_buf: StrTextBuf::at_end("snan"),
772                    nan_header: ParsedNanHeader {
773                        is_nan_signaling: true,
774                        is_nan_negative: false,
775                    },
776                    nan_payload: None,
777                },
778            ),
779            (
780                "-snan",
781                ParsedNan {
782                    nan_buf: StrTextBuf::at_end("-snan"),
783                    nan_header: ParsedNanHeader {
784                        is_nan_signaling: true,
785                        is_nan_negative: true,
786                    },
787                    nan_payload: None,
788                },
789            ),
790            (
791                "+snan",
792                ParsedNan {
793                    nan_buf: StrTextBuf::at_end("+snan"),
794                    nan_header: ParsedNanHeader {
795                        is_nan_signaling: true,
796                        is_nan_negative: false,
797                    },
798                    nan_payload: None,
799                },
800            ),
801            (
802                "NaN",
803                ParsedNan {
804                    nan_buf: StrTextBuf::at_end("NaN"),
805                    nan_header: ParsedNanHeader {
806                        is_nan_signaling: false,
807                        is_nan_negative: false,
808                    },
809                    nan_payload: None,
810                },
811            ),
812            (
813                "SNAN",
814                ParsedNan {
815                    nan_buf: StrTextBuf::at_end("SNAN"),
816                    nan_header: ParsedNanHeader {
817                        is_nan_signaling: true,
818                        is_nan_negative: false,
819                    },
820                    nan_payload: None,
821                },
822            ),
823            (
824                "nan(1234)",
825                ParsedNan {
826                    nan_buf: StrTextBuf::at_end("nan(1234)"),
827                    nan_header: ParsedNanHeader {
828                        is_nan_signaling: false,
829                        is_nan_negative: false,
830                    },
831                    nan_payload: Some(ParsedSignificand {
832                        significand_range: 4..8,
833                        ..Default::default()
834                    }),
835                },
836            ),
837            (
838                "nan()",
839                ParsedNan {
840                    nan_buf: StrTextBuf::at_end("nan()"),
841                    nan_header: ParsedNanHeader {
842                        is_nan_signaling: false,
843                        is_nan_negative: false,
844                    },
845                    nan_payload: Some(ParsedSignificand {
846                        significand_range: 4..4,
847                        ..Default::default()
848                    }),
849                },
850            ),
851        ] {
852            let mut parser = NanParser::begin(StrTextBuf::new(input));
853            let _ = parser.write_str(input);
854
855            let nan = parser.end().expect("failed to parse");
856
857            assert_eq!(expected, &nan, "{}", input);
858        }
859    }
860
861    #[test]
862    fn parse_fails_on_buffer_too_small() {
863        let expected_err = "the buffer is too small";
864
865        let mut buf = DecimalParser::begin(ArrayTextBuf::<1>::default());
866        let err = buf.parse_ascii(b"123").unwrap_err();
867        assert_eq!(expected_err, &err.to_string());
868
869        let mut buf = FiniteParser::begin(ArrayTextBuf::<1>::default());
870        let err = buf.parse_ascii(b"123").unwrap_err();
871        assert_eq!(expected_err, &err.to_string());
872
873        let mut buf = InfinityParser::begin(ArrayTextBuf::<1>::default());
874        let err = buf.parse_ascii(b"inf").unwrap_err();
875        assert_eq!(expected_err, &err.to_string());
876
877        let mut buf = NanParser::begin(ArrayTextBuf::<1>::default());
878        let err = buf.parse_ascii(b"nan").unwrap_err();
879        assert_eq!(expected_err, &err.to_string());
880    }
881
882    #[test]
883    fn parse_finite_invalid() {
884        let (input, expected_err) = &("", "unexpected end of input, expected a sign or digit");
885        let actual_err = FiniteParser::parse_str(input).unwrap_err();
886
887        assert_eq!(expected_err, &actual_err.to_string(), "{}", input);
888    }
889
890    #[test]
891    fn parse_invalid() {
892        for (input, expected_err) in &[
893            (
894                "",
895                "unexpected end of input, expected a finite number, infinity, or NaN",
896            ),
897            (
898                "-",
899                "unexpected end of input, expected a finite number, infinity, or NaN",
900            ),
901            (
902                "+",
903                "unexpected end of input, expected a finite number, infinity, or NaN",
904            ),
905            ("1e", "unexpected end of input, expected a sign or digit"),
906            ("1e-", "unexpected end of input, expected any digit"),
907            ("1e+", "unexpected end of input, expected any digit"),
908            ("in", "unexpected end of input, expected `f`"),
909            ("n", "unexpected end of input, expected `a`"),
910            ("s", "unexpected end of input, expected `n`"),
911            ("nan(", "unexpected end of input, expected `)`"),
912            ("nan(123", "unexpected end of input, expected `)`"),
913            ("snan(", "unexpected end of input, expected `)`"),
914            ("snan(123", "unexpected end of input, expected `)`"),
915            (
916                "x",
917                "unexpected character `x`, expected a finite number, infinity, or NaN",
918            ),
919            (
920                "-x",
921                "unexpected character `x`, expected a finite number, infinity, or NaN",
922            ),
923            (
924                "+x",
925                "unexpected character `x`, expected a finite number, infinity, or NaN",
926            ),
927            ("1x", "unexpected character `x`, expected any digit"),
928            ("1ex", "unexpected character `x`, expected any digit"),
929            ("1e-x", "unexpected character `x`, expected any digit"),
930            ("1e+x", "unexpected character `x`, expected any digit"),
931            ("1.", "unexpected end of input, expected a sign or digit"),
932            ("inx", "unexpected character `x`, expected `f`"),
933            ("infinityx", "unexpected character `x`"),
934            ("nx", "unexpected character `x`"),
935            ("snx", "unexpected character `x`"),
936            ("nan()x", "unexpected character `x`"),
937            ("snan()x", "unexpected character `x`"),
938            (
939                "--",
940                "unexpected character `-`, expected a finite number, infinity, or NaN",
941            ),
942            (
943                "++",
944                "unexpected character `+`, expected a finite number, infinity, or NaN",
945            ),
946            (
947                "-+",
948                "unexpected character `+`, expected a finite number, infinity, or NaN",
949            ),
950            (
951                "+-",
952                "unexpected character `-`, expected a finite number, infinity, or NaN",
953            ),
954            (
955                ".",
956                "unexpected character `.`, expected a finite number, infinity, or NaN",
957            ),
958            (
959                "..",
960                "unexpected character `.`, expected a finite number, infinity, or NaN",
961            ),
962            ("1.3.2", "unexpected character `.`, expected any digit"),
963            ("1e1.1", "unexpected character `.`, expected any digit"),
964            ("1-", "unexpected character `-`, expected any digit"),
965            ("1+", "unexpected character `+`, expected any digit"),
966            ("1e1-", "unexpected character `-`, expected any digit"),
967            ("1e1+", "unexpected character `+`, expected any digit"),
968            ("n(", "unexpected character `(`"),
969            ("sn(", "unexpected character `(`"),
970            ("nan(1.2)", "unexpected character `.`"),
971            ("snan(1.2)", "unexpected character `.`"),
972            ("nan(-1)", "unexpected character `-`"),
973            ("snan(-1)", "unexpected character `-`"),
974            ("nan(1e2", "unexpected character `e`"),
975            ("snan(1e2)", "unexpected character `e`"),
976        ] {
977            let actual_err = DecimalParser::parse_str(input).unwrap_err();
978
979            assert_eq!(expected_err, &actual_err.to_string(), "{}", input);
980        }
981    }
982
983    #[test]
984    fn parse_fmt_no_content() {
985        struct Empty;
986
987        impl fmt::Display for Empty {
988            fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
989                Ok(())
990            }
991        }
992
993        let mut parser = DecimalParser::begin(ArrayTextBuf::<32>::default());
994        parser.parse_fmt(Empty).unwrap();
995
996        let expected_err = "unexpected end of input, expected a finite number, infinity, or NaN";
997
998        assert_eq!(expected_err, &parser.end().unwrap_err().to_string());
999    }
1000
1001    #[test]
1002    fn parse_fmt_failing() {
1003        struct Failing;
1004
1005        impl fmt::Display for Failing {
1006            fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
1007                Err(fmt::Error)
1008            }
1009        }
1010
1011        let mut parser = DecimalParser::begin(ArrayTextBuf::<32>::default());
1012
1013        let expected_err = "the source produced an error while parsing";
1014
1015        assert_eq!(
1016            expected_err,
1017            &parser.parse_fmt(Failing).unwrap_err().to_string()
1018        );
1019    }
1020}