utils/parser/
number.rs

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