//! Parse and validate numeric inputs.

/// A validated numeric input.
///
/// Holds the integer value used for arithmetic. The raw string form is kept
/// separately by the caller because padding, length, and literal output depend
/// on the exact characters passed in.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Number {
    /// The parsed integer value.
    pub value: i64,
}

impl Number {
    /// Parse a decimal integer string.
    ///
    /// Accepts an optional leading `-`, optional leading zeros, and surrounding
    /// whitespace. Returns `None` for blank strings, non-numeric strings, a lone
    /// sign, values outside `i64`, and `i64::MIN`, whose negation overflows
    /// `i64` and so cannot drive the pattern builder.
    ///
    /// This is the decimal-only analog of the source predicate. The source also
    /// accepted JS coercion forms like `1e3` and `0x10`. Those forms are out of
    /// scope here. Inputs are plain decimal integers.
    pub fn parse(raw: &str) -> Option<Number> {
        let trimmed = raw.trim();
        if trimmed.is_empty() {
            return None;
        }

        let digits = trimmed.strip_prefix('-').unwrap_or(trimmed);
        if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) {
            return None;
        }

        trimmed
            .parse::<i64>()
            .ok()
            .filter(|&value| value != i64::MIN)
            .map(|value| Number { value })
    }
}
