1
//! C-compatible (`#[repr(C)]`) error types for CSS parsing failures.
2
//!
3
//! Mirrors `core::num::ParseFloatError` and `core::num::ParseIntError` for FFI use,
4
//! and provides generic invalid-value error wrappers.
5

            
6
use crate::corety::AzString;
7

            
8
/// Simple "invalid value" error, used for basic parsing failures
9
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
10
pub struct InvalidValueErr<'a>(pub &'a str);
11

            
12
/// Owned version of InvalidValueErr with AzString.
13
#[derive(Debug, Clone, PartialEq)]
14
#[repr(C)]
15
pub struct InvalidValueErrOwned {
16
    pub value: AzString,
17
}
18

            
19
/// C-compatible enum mirroring `core::num::ParseFloatError` internals.
20
///
21
/// `core::num::ParseFloatError` is a 1-byte enum with variants `Empty` and `Invalid`,
22
/// but its `kind` field is private. We mirror the variants here for FFI compatibility.
23
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
24
#[repr(C)]
25
pub enum ParseFloatError {
26
    /// Input string was empty.
27
    Empty,
28
    /// Input string was not a valid float literal.
29
    Invalid,
30
}
31

            
32
impl ParseFloatError {
33
    /// Convert from `core::num::ParseFloatError` by comparing against known error instances.
34
3
    fn from_std(e: &core::num::ParseFloatError) -> Self {
35
        // Compare against the known Empty error instance to avoid
36
        // relying on Display message wording or allocating a format string.
37
3
        let empty_err = "".parse::<f32>().unwrap_err();
38
3
        if *e == empty_err {
39
            ParseFloatError::Empty
40
        } else {
41
3
            ParseFloatError::Invalid
42
        }
43
3
    }
44

            
45
    /// Reconstruct a `core::num::ParseFloatError` from our C-compatible variant.
46
    pub fn to_std(&self) -> core::num::ParseFloatError {
47
        match self {
48
            ParseFloatError::Empty => "".parse::<f32>().unwrap_err(),
49
            ParseFloatError::Invalid => "x".parse::<f32>().unwrap_err(),
50
        }
51
    }
52
}
53

            
54
impl core::fmt::Display for ParseFloatError {
55
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
56
        match self {
57
            ParseFloatError::Empty => write!(f, "cannot parse float from empty string"),
58
            ParseFloatError::Invalid => write!(f, "invalid float literal"),
59
        }
60
    }
61
}
62

            
63
impl From<core::num::ParseFloatError> for ParseFloatError {
64
3
    fn from(e: core::num::ParseFloatError) -> Self {
65
3
        ParseFloatError::from_std(&e)
66
3
    }
67
}
68

            
69
/// C-compatible enum mirroring `core::num::ParseIntError` internals.
70
///
71
/// `core::num::ParseIntError` is a 1-byte enum with variants matching `IntErrorKind`.
72
/// We mirror them here for FFI compatibility.
73
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
74
#[repr(C)]
75
pub enum ParseIntError {
76
    /// Input string was empty.
77
    Empty,
78
    /// Input contained an invalid digit.
79
    InvalidDigit,
80
    /// Input overflowed the target integer type (positive).
81
    PosOverflow,
82
    /// Input overflowed the target integer type (negative).
83
    NegOverflow,
84
    /// Input was zero but zero is not allowed (rarely used).
85
    Zero,
86
}
87

            
88
impl ParseIntError {
89
    /// Convert from `core::num::ParseIntError` using the stable `kind()` method.
90
2
    fn from_std(e: &core::num::ParseIntError) -> Self {
91
        use core::num::IntErrorKind;
92
2
        match e.kind() {
93
            IntErrorKind::Empty => ParseIntError::Empty,
94
1
            IntErrorKind::InvalidDigit => ParseIntError::InvalidDigit,
95
1
            IntErrorKind::PosOverflow => ParseIntError::PosOverflow,
96
            IntErrorKind::NegOverflow => ParseIntError::NegOverflow,
97
            IntErrorKind::Zero => ParseIntError::Zero,
98
            _ => ParseIntError::InvalidDigit, // future-proofing
99
        }
100
2
    }
101

            
102
    /// Reconstruct a `core::num::ParseIntError` from our C-compatible variant.
103
    pub fn to_std(&self) -> core::num::ParseIntError {
104
        match self {
105
            ParseIntError::Empty => "".parse::<i32>().unwrap_err(),
106
            ParseIntError::InvalidDigit => "x".parse::<i32>().unwrap_err(),
107
            ParseIntError::PosOverflow => "99999999999999999999".parse::<i32>().unwrap_err(),
108
            ParseIntError::NegOverflow => "-99999999999999999999".parse::<i32>().unwrap_err(),
109
            ParseIntError::Zero => {
110
                // Zero variant cannot be reproduced on stable Rust; falls back to InvalidDigit.
111
                // Note: round-tripping Zero through to_std() then from_std() yields InvalidDigit.
112
                "x".parse::<i32>().unwrap_err()
113
            }
114
        }
115
    }
116
}
117

            
118
impl core::fmt::Display for ParseIntError {
119
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
120
        match self {
121
            ParseIntError::Empty => write!(f, "cannot parse integer from empty string"),
122
            ParseIntError::InvalidDigit => write!(f, "invalid digit found in string"),
123
            ParseIntError::PosOverflow => write!(f, "number too large to fit in target type"),
124
            ParseIntError::NegOverflow => write!(f, "number too small to fit in target type"),
125
            ParseIntError::Zero => write!(f, "number would be zero for non-zero type"),
126
        }
127
    }
128
}
129

            
130
impl From<core::num::ParseIntError> for ParseIntError {
131
2
    fn from(e: core::num::ParseIntError) -> Self {
132
2
        ParseIntError::from_std(&e)
133
2
    }
134
}
135

            
136
/// Wrapper for a ParseFloatError paired with the input string that failed.
137
/// Used by multiple Owned error enums that need to store both the error and input.
138
#[derive(Debug, Clone, PartialEq)]
139
#[repr(C)]
140
pub struct ParseFloatErrorWithInput {
141
    pub error: ParseFloatError,
142
    pub input: AzString,
143
}
144

            
145
/// Wrapper for WrongNumberOfComponents errors in CSS filter/transform parsing.
146
#[derive(Debug, Clone, PartialEq)]
147
#[repr(C)]
148
pub struct WrongComponentCountError {
149
    pub expected: usize,
150
    pub got: usize,
151
    pub input: AzString,
152
}
153

            
154
impl<'a> InvalidValueErr<'a> {
155
    pub fn to_contained(&self) -> InvalidValueErrOwned {
156
        InvalidValueErrOwned { value: self.0.to_string().into() }
157
    }
158
}
159

            
160
impl InvalidValueErrOwned {
161
    pub fn to_shared<'a>(&'a self) -> InvalidValueErr<'a> {
162
        InvalidValueErr(self.value.as_str())
163
    }
164
}