utils/parser/
number.rs

1use crate::number::{Integer, SignedInteger, UnsignedInteger};
2use crate::parser::then::Then2;
3use crate::parser::{ParseError, ParseResult, Parseable, Parser};
4use std::marker::PhantomData;
5use std::ops::RangeInclusive;
6
7#[derive(Copy, Clone)]
8pub struct UnsignedParser<U: UnsignedInteger>(PhantomData<U>);
9impl<'i, U: UnsignedInteger> Parser<'i> for UnsignedParser<U> {
10    type Output = U;
11    type Then<T: Parser<'i>> = Then2<Self, T>;
12
13    #[inline]
14    fn parse(&self, mut input: &'i [u8]) -> ParseResult<'i, Self::Output> {
15        let mut n = match input {
16            [d @ b'0'..=b'9', ..] => {
17                input = &input[1..];
18                U::from(d - b'0')
19            }
20            _ => return Err((ParseError::Expected("unsigned integer"), input)),
21        };
22
23        while let Some(d @ b'0'..=b'9') = input.first() {
24            n = n
25                .checked_mul(U::from(10))
26                .and_then(|n| n.checked_add(U::from(d - b'0')))
27                .ok_or((ParseError::too_large(U::MAX), input))?;
28            input = &input[1..];
29        }
30
31        Ok((n, input))
32    }
33}
34
35#[derive(Copy, Clone)]
36pub struct SignedParser<S: SignedInteger>(PhantomData<S>);
37impl<'i, S: SignedInteger> Parser<'i> for SignedParser<S> {
38    type Output = S;
39    type Then<T: Parser<'i>> = Then2<Self, T>;
40
41    #[expect(clippy::cast_possible_wrap)]
42    #[inline]
43    fn parse(&self, mut input: &'i [u8]) -> ParseResult<'i, Self::Output> {
44        let (mut n, positive) = match input {
45            [d @ b'0'..=b'9', rem @ ..] | [b'+', d @ b'0'..=b'9', rem @ ..] => {
46                input = rem;
47                (S::from((d - b'0') as i8), true)
48            }
49            [b'-', d @ b'0'..=b'9', rem @ ..] => {
50                input = rem;
51                (S::from(-((d - b'0') as i8)), false)
52            }
53            _ => return Err((ParseError::Expected("signed integer"), input)),
54        };
55
56        if positive {
57            while let Some(d @ b'0'..=b'9') = input.first() {
58                n = n
59                    .checked_mul(S::from(10))
60                    .and_then(|n| n.checked_add(S::from((d - b'0') as i8)))
61                    .ok_or((ParseError::too_large(S::MAX), input))?;
62                input = &input[1..];
63            }
64        } else {
65            while let Some(d @ b'0'..=b'9') = input.first() {
66                n = n
67                    .checked_mul(S::from(10))
68                    .and_then(|n| n.checked_sub(S::from((d - b'0') as i8)))
69                    .ok_or((ParseError::too_small(S::MIN), input))?;
70                input = &input[1..];
71            }
72        }
73
74        Ok((n, input))
75    }
76}
77
78macro_rules! parser_for {
79    ($p:ident => $($n:ident),+) => {$(
80        impl Parseable for std::primitive::$n {
81            type Parser = $p<std::primitive::$n>;
82            const PARSER: Self::Parser = $p(PhantomData);
83        }
84
85        #[doc = concat!("Parser for [`prim@", stringify!($n), "`] values.")]
86        #[inline]
87        #[must_use]
88        pub fn $n() -> $p<std::primitive::$n> {
89            $p(PhantomData)
90        }
91    )+};
92}
93parser_for! { UnsignedParser => u8, u16, u32, u64, u128 }
94parser_for! { SignedParser => i8, i16, i32, i64, i128 }
95
96/// Parsing as [`usize`] should be discouraged as it leads to parsers which behave differently at
97/// runtime on 32-bit and 64-bit platforms, so no `parser::usize()` function is provided.
98///
99/// However, [`Parseable`] is implemented for [`usize`] as it is safe to use [`number_range()`]
100/// with a constant hard-coded max, which will fail at compile time if the constant is too large
101/// for the platform's usize.
102impl Parseable for std::primitive::usize {
103    type Parser = UnsignedParser<std::primitive::usize>;
104    const PARSER: Self::Parser = UnsignedParser(PhantomData);
105}
106
107#[derive(Copy, Clone)]
108pub struct NumberRange<I> {
109    min: I,
110    max: I,
111}
112
113impl<'i, I: Integer + Parseable> Parser<'i> for NumberRange<I> {
114    type Output = I;
115    type Then<T: Parser<'i>> = Then2<Self, T>;
116
117    #[inline]
118    fn parse(&self, input: &'i [u8]) -> ParseResult<'i, Self::Output> {
119        let (v, remaining) = I::PARSER.parse(input)?;
120        if v < self.min {
121            Err((ParseError::too_small(self.min), input))
122        } else if v > self.max {
123            Err((ParseError::too_large(self.max), input))
124        } else {
125            Ok((v, remaining))
126        }
127    }
128}
129
130/// Parser for numbers in the supplied range.
131///
132/// The type of the number to parse is inferred from the range's type.
133///
134/// See also [`byte_range`](super::byte_range).
135///
136/// # Examples
137/// ```
138/// # use utils::parser::{self, Parser};
139/// assert_eq!(
140///     parser::number_range(100u8..=125u8).parse(b"123, 120"),
141///     Ok((123u8, &b", 120"[..]))
142/// );
143/// ```
144#[inline]
145#[must_use]
146pub fn number_range<I: Integer + Parseable>(range: RangeInclusive<I>) -> NumberRange<I> {
147    let min = *range.start();
148    let max = *range.end();
149    assert!(min <= max);
150    NumberRange { min, max }
151}
152
153#[derive(Copy, Clone)]
154pub struct Digit {}
155
156impl<'i> Parser<'i> for Digit {
157    type Output = u8;
158    type Then<T: Parser<'i>> = Then2<Self, T>;
159
160    #[inline]
161    fn parse(&self, input: &'i [u8]) -> ParseResult<'i, Self::Output> {
162        if let Some(d @ b'0'..=b'9') = input.first() {
163            Ok((d - b'0', &input[1..]))
164        } else {
165            Err((ParseError::Expected("digit"), input))
166        }
167    }
168}
169
170/// Parser for single digits.
171///
172/// # Examples
173/// ```
174/// # use utils::parser::{self, Parser};
175/// assert_eq!(
176///     parser::digit().parse(b"12345"),
177///     Ok((1u8, &b"2345"[..]))
178/// );
179/// ```
180#[inline]
181#[must_use]
182pub fn digit() -> Digit {
183    Digit {}
184}