Skip to main content

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
75#[derive(Copy, Clone)]
76pub struct NonZeroParser<I: Integer + Parseable>(PhantomData<I>);
77impl<'i, I: Integer + Parseable> Leaf<'i> for NonZeroParser<I> {
78    type Output = I::NonZero;
79
80    #[inline]
81    fn parse(&self, input: &'i [u8]) -> LeafResult<'i, Self::Output> {
82        let (value, remaining) = I::PARSER.parse(input)?;
83        match I::NonZero::try_from(value) {
84            Ok(value) => Ok((value, remaining)),
85            Err(_) => Err((ParseError::Expected("non-zero integer"), input)),
86        }
87    }
88}
89
90macro_rules! parser_for {
91    ($p:ident => $($n:ident: $nonzero:ident),+ $(,)?) => {$(
92        impl Parseable for std::primitive::$n {
93            type Parser = $p<std::primitive::$n>;
94            const PARSER: Self::Parser = $p(PhantomData);
95        }
96
97        #[doc = concat!("[`Leaf`] parser for [`prim@", stringify!($n), "`] values.")]
98        #[inline]
99        #[must_use]
100        pub fn $n() -> $p<std::primitive::$n> {
101            $p(PhantomData)
102        }
103
104        impl Parseable for std::num::NonZero<$n> {
105            type Parser = NonZeroParser<$n>;
106            const PARSER: Self::Parser = NonZeroParser(PhantomData);
107        }
108
109        #[doc = concat!(
110            "[`Leaf`] parser for [`NonZero<", stringify!($n), ">`](std::num::NonZero) values.",
111            "\n\n# Examples\n```\n",
112            "# use utils::parser::{self, Parser};\n",
113            "assert!(parser::", stringify!($nonzero), "().parse_complete(\"0\").is_err());\n",
114            "assert!(parser::", stringify!($nonzero), "().parse_complete(\"1\").is_ok_and(|n| n.get() == 1));\n",
115            "```"
116        )]
117        #[inline]
118        #[must_use]
119        pub fn $nonzero() -> NonZeroParser<std::primitive::$n> {
120            NonZeroParser(PhantomData)
121        }
122    )+};
123}
124parser_for! {
125    UnsignedParser =>
126    u8: nonzero_u8,
127    u16: nonzero_u16,
128    u32: nonzero_u32,
129    u64: nonzero_u64,
130    u128: nonzero_u128,
131}
132parser_for! {
133    SignedParser =>
134    i8: nonzero_i8,
135    i16: nonzero_i16,
136    i32: nonzero_i32,
137    i64: nonzero_i64,
138    i128: nonzero_i128,
139}
140
141/// Parsing as [`usize`] should be discouraged as it leads to parsers which behave differently at
142/// runtime on 32-bit and 64-bit platforms, so no `parser::usize()` function is provided.
143///
144/// However, [`Parseable`] is implemented for [`usize`] as it is safe to use [`number_range()`]
145/// with a constant hard-coded max, which will fail at compile time if the constant is too large
146/// for the platform's usize.
147impl Parseable for std::primitive::usize {
148    type Parser = UnsignedParser<std::primitive::usize>;
149    const PARSER: Self::Parser = UnsignedParser(PhantomData);
150}
151
152#[derive(Copy, Clone)]
153pub struct NumberRange<I> {
154    min: I,
155    max: I,
156}
157
158impl<'i, I: Integer + Parseable> Leaf<'i> for NumberRange<I> {
159    type Output = I;
160
161    #[inline]
162    fn parse(&self, input: &'i [u8]) -> LeafResult<'i, Self::Output> {
163        let (v, remaining) = I::PARSER.parse(input)?;
164        if v < self.min {
165            Err((ParseError::too_small(self.min), input))
166        } else if v > self.max {
167            Err((ParseError::too_large(self.max), input))
168        } else {
169            Ok((v, remaining))
170        }
171    }
172}
173
174/// [`Leaf`] parser for numbers in the supplied range.
175///
176/// The type of the number to parse is inferred from the range's type.
177///
178/// See also [`byte_range`](super::byte_range).
179///
180/// # Examples
181/// ```
182/// # use utils::parser::{self, Leaf};
183/// assert_eq!(
184///     parser::number_range(100u8..=125u8).parse(b"123, 120"),
185///     Ok((123u8, &b", 120"[..]))
186/// );
187/// ```
188#[inline]
189#[must_use]
190pub fn number_range<I: Integer + Parseable>(range: RangeInclusive<I>) -> NumberRange<I> {
191    let min = *range.start();
192    let max = *range.end();
193    assert!(min <= max);
194    NumberRange { min, max }
195}
196
197#[derive(Copy, Clone)]
198pub struct Digit {}
199
200impl<'i> Leaf<'i> for Digit {
201    type Output = u8;
202
203    #[inline]
204    fn parse(&self, input: &'i [u8]) -> LeafResult<'i, Self::Output> {
205        if let Some(d @ b'0'..=b'9') = input.first() {
206            Ok((d - b'0', &input[1..]))
207        } else {
208            Err((ParseError::Expected("digit"), input))
209        }
210    }
211}
212
213/// [`Leaf`] parser for single digits.
214///
215/// # Examples
216/// ```
217/// # use utils::parser::{self, Leaf};
218/// assert_eq!(
219///     parser::digit().parse(b"12345"),
220///     Ok((1u8, &b"2345"[..]))
221/// );
222/// ```
223#[inline]
224#[must_use]
225pub fn digit() -> Digit {
226    Digit {}
227}