rosu_map/util/
parse_number.rs

1use std::num;
2
3/// The default limit when parsing via [`ParseNumber`].
4pub const MAX_PARSE_VALUE: i32 = i32::MAX;
5
6/// Parses a `&str` to a number and makes sure it doesn't exceed a limit.
7pub trait ParseNumber: Sized {
8    /// Parses a number without exceeding [`MAX_PARSE_VALUE`].
9    fn parse(s: &str) -> Result<Self, ParseNumberError>;
10
11    /// Parses a number without exceeding the given limit.
12    fn parse_with_limits(s: &str, limit: Self) -> Result<Self, ParseNumberError>;
13}
14
15thiserror! {
16    /// All the ways that parsing with [`ParseNumber`] can fail.
17    #[derive(Debug)]
18    pub enum ParseNumberError {
19        #[error("invalid float")]
20        InvalidFloat(#[from] num::ParseFloatError),
21        #[error("invalid integer")]
22        InvalidInteger(#[from] num::ParseIntError),
23        #[error("not a number")]
24        NaN,
25        #[error("value is too high")]
26        NumberOverflow,
27        #[error("value is too low")]
28        NumberUnderflow,
29    }
30}
31
32impl ParseNumber for i32 {
33    fn parse(s: &str) -> Result<Self, ParseNumberError> {
34        Self::parse_with_limits(s, MAX_PARSE_VALUE)
35    }
36
37    fn parse_with_limits(s: &str, limit: Self) -> Result<Self, ParseNumberError> {
38        let n: Self = s.trim().parse()?;
39
40        if n < -limit {
41            Err(ParseNumberError::NumberUnderflow)
42        } else if n > limit {
43            Err(ParseNumberError::NumberOverflow)
44        } else {
45            Ok(n)
46        }
47    }
48}
49
50impl ParseNumber for f32 {
51    fn parse(s: &str) -> Result<Self, ParseNumberError> {
52        Self::parse_with_limits(s, MAX_PARSE_VALUE as Self)
53    }
54
55    fn parse_with_limits(s: &str, limit: Self) -> Result<Self, ParseNumberError> {
56        let n: Self = s.trim().parse()?;
57
58        if n < -limit {
59            Err(ParseNumberError::NumberUnderflow)
60        } else if n > limit {
61            Err(ParseNumberError::NumberOverflow)
62        } else if n.is_nan() {
63            Err(ParseNumberError::NaN)
64        } else {
65            Ok(n)
66        }
67    }
68}
69
70impl ParseNumber for f64 {
71    fn parse(s: &str) -> Result<Self, ParseNumberError> {
72        Self::parse_with_limits(s, Self::from(MAX_PARSE_VALUE))
73    }
74
75    fn parse_with_limits(s: &str, limit: Self) -> Result<Self, ParseNumberError> {
76        let n: Self = s.trim().parse()?;
77
78        if n < -limit {
79            Err(ParseNumberError::NumberUnderflow)
80        } else if n > limit {
81            Err(ParseNumberError::NumberOverflow)
82        } else if n.is_nan() {
83            Err(ParseNumberError::NaN)
84        } else {
85            Ok(n)
86        }
87    }
88}