scolapasta_int_parse/
parser.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use alloc::string::String;

use crate::error::ArgumentError;
use crate::subject::IntegerString;

#[must_use]
#[derive(Default, Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum Sign {
    #[default]
    Positive,
    Negative,
}

#[must_use]
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum State<'a> {
    Initial(IntegerString<'a>),
    Sign(IntegerString<'a>, Sign),
    Accumulate(IntegerString<'a>, String),
}

impl<'a> State<'a> {
    #[inline]
    pub fn new(subject: IntegerString<'a>) -> Self {
        Self::Initial(subject)
    }

    pub fn set_sign(self, sign: Sign) -> Result<Self, ArgumentError<'a>> {
        match self {
            Self::Sign(subject, _) | Self::Accumulate(subject, _) => Err(subject.into()),
            Self::Initial(subject) => Ok(Self::Sign(subject, sign)),
        }
    }

    pub fn collect_digit(self, digit: u8) -> Self {
        // For `i64::MIN` and `i64::MAX` in base 10:
        //
        // ```
        // [3.1.2] > 2 ** 64 - 1
        // => 18446744073709551615
        // [3.1.2] > (2 ** 64 - 1).to_s.length
        // => 20
        // [3.1.2] > -(2 ** 64)
        // => -18446744073709551616
        // [3.1.2] > (-(2 ** 64 - 1)).to_s.length
        // => 21
        // ```
        //
        // In bases below 10, the string representation for large numbers will
        // be longer, but pre-allocating for these uncommon cases seems wasteful.
        // The `String` will reallocate if it needs to in these pathological cases.
        const PRE_ALLOCATED_DIGIT_CAPACITY: usize = 21;

        match self {
            Self::Initial(arg) => {
                let mut digits = String::with_capacity(PRE_ALLOCATED_DIGIT_CAPACITY);
                digits.push(char::from(digit));
                Self::Accumulate(arg, digits)
            }
            Self::Sign(arg, sign) => {
                let mut digits = String::with_capacity(PRE_ALLOCATED_DIGIT_CAPACITY);
                if let Sign::Negative = sign {
                    digits.push('-');
                }
                digits.push(char::from(digit));
                Self::Accumulate(arg, digits)
            }
            Self::Accumulate(arg, mut digits) => {
                digits.push(char::from(digit));
                Self::Accumulate(arg, digits)
            }
        }
    }

    pub fn into_numeric_string(self) -> Result<String, ArgumentError<'a>> {
        match self {
            Self::Accumulate(_, digits) => Ok(digits),
            Self::Initial(subject) | Self::Sign(subject, _) => Err(subject.into()),
        }
    }
}