Skip to main content

azul_css/props/basic/
error.rs

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
6use crate::corety::AzString;
7
8/// Simple "invalid value" error, used for basic parsing failures
9#[derive(Debug, Copy, Clone, Eq, PartialEq)]
10pub struct InvalidValueErr<'a>(pub &'a str);
11
12/// Owned version of InvalidValueErr with AzString.
13#[derive(Debug, Clone, PartialEq)]
14#[repr(C)]
15pub 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)]
25pub enum ParseFloatError {
26    /// Input string was empty.
27    Empty,
28    /// Input string was not a valid float literal.
29    Invalid,
30}
31
32impl ParseFloatError {
33    /// Convert from `core::num::ParseFloatError` by comparing against known error instances.
34    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        let empty_err = "".parse::<f32>().unwrap_err();
38        if *e == empty_err {
39            ParseFloatError::Empty
40        } else {
41            ParseFloatError::Invalid
42        }
43    }
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
54impl 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
63impl From<core::num::ParseFloatError> for ParseFloatError {
64    fn from(e: core::num::ParseFloatError) -> Self {
65        ParseFloatError::from_std(&e)
66    }
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)]
75pub 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
88impl ParseIntError {
89    /// Convert from `core::num::ParseIntError` using the stable `kind()` method.
90    fn from_std(e: &core::num::ParseIntError) -> Self {
91        use core::num::IntErrorKind;
92        match e.kind() {
93            IntErrorKind::Empty => ParseIntError::Empty,
94            IntErrorKind::InvalidDigit => ParseIntError::InvalidDigit,
95            IntErrorKind::PosOverflow => ParseIntError::PosOverflow,
96            IntErrorKind::NegOverflow => ParseIntError::NegOverflow,
97            IntErrorKind::Zero => ParseIntError::Zero,
98            _ => ParseIntError::InvalidDigit, // future-proofing
99        }
100    }
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
118impl 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
130impl From<core::num::ParseIntError> for ParseIntError {
131    fn from(e: core::num::ParseIntError) -> Self {
132        ParseIntError::from_std(&e)
133    }
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)]
140pub 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)]
148pub struct WrongComponentCountError {
149    pub expected: usize,
150    pub got: usize,
151    pub input: AzString,
152}
153
154impl<'a> InvalidValueErr<'a> {
155    pub fn to_contained(&self) -> InvalidValueErrOwned {
156        InvalidValueErrOwned { value: self.0.to_string().into() }
157    }
158}
159
160impl InvalidValueErrOwned {
161    pub fn to_shared<'a>(&'a self) -> InvalidValueErr<'a> {
162        InvalidValueErr(self.value.as_str())
163    }
164}