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<U: UnsignedInteger> Parser for UnsignedParser<U> {
10    type Output<'i> = U;
11    type Then<T: Parser> = Then2<Self, T>;
12
13    #[inline]
14    fn parse<'i>(&self, mut input: &'i [u8]) -> ParseResult<'i, Self::Output<'i>> {
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<S: SignedInteger> Parser for SignedParser<S> {
38    type Output<'i> = S;
39    type Then<T: Parser> = Then2<Self, T>;
40
41    #[expect(clippy::cast_possible_wrap)]
42    #[inline]
43    fn parse<'i>(&self, mut input: &'i [u8]) -> ParseResult<'i, Self::Output<'i>> {
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        #[must_use]
87        pub fn $n() -> $p<std::primitive::$n> {
88            $p(PhantomData)
89        }
90    )+};
91}
92parser_for! { UnsignedParser => u8, u16, u32, u64, u128 }
93parser_for! { SignedParser => i8, i16, i32, i64, i128 }
94
95/// Parsing as [`usize`] should be discouraged as it leads to parsers which behave differently at
96/// runtime on 32-bit and 64-bit platforms, so no `parser::usize()` function is provided.
97///
98/// However, [`Parseable`] is implemented for [`usize`] as it is safe to use [`number_range()`]
99/// with a constant hard-coded max, which will fail at compile time if the constant is too large
100/// for the platform's usize.
101impl Parseable for std::primitive::usize {
102    type Parser = UnsignedParser<std::primitive::usize>;
103    const PARSER: Self::Parser = UnsignedParser(PhantomData);
104}
105
106#[derive(Copy, Clone)]
107pub struct NumberRange<I> {
108    min: I,
109    max: I,
110}
111
112impl<I: Integer + Parseable> Parser for NumberRange<I> {
113    type Output<'i> = I;
114    type Then<T: Parser> = Then2<Self, T>;
115
116    fn parse<'i>(&self, input: &'i [u8]) -> ParseResult<'i, Self::Output<'i>> {
117        let (v, remaining) = I::PARSER.parse(input)?;
118        if v < self.min {
119            Err((ParseError::too_small(self.min), input))
120        } else if v > self.max {
121            Err((ParseError::too_large(self.max), input))
122        } else {
123            Ok((v, remaining))
124        }
125    }
126}
127
128/// Parser for numbers in the supplied range.
129///
130/// The type of the number to parse is inferred from the range's type.
131///
132/// See also [`byte_range`](super::byte_range).
133///
134/// # Examples
135/// ```
136/// # use utils::parser::{self, Parser};
137/// assert_eq!(
138///     parser::number_range(100u8..=125u8).parse(b"123, 120"),
139///     Ok((123u8, &b", 120"[..]))
140/// );
141/// ```
142#[inline]
143#[must_use]
144pub fn number_range<I: Integer + Parseable>(range: RangeInclusive<I>) -> NumberRange<I> {
145    let min = *range.start();
146    let max = *range.end();
147    assert!(min <= max);
148    NumberRange { min, max }
149}