1use alloc::string::{String, ToString};
6use crate::corety::AzString;
7
8use crate::{
9 props::{
10 basic::pixel::{
11 parse_pixel_value, CssPixelValueParseError, CssPixelValueParseErrorOwned, PixelValue,
12 },
13 macros::PixelValueTaker,
14 },
15};
16
17macro_rules! define_border_radius_property {
20 ($struct_name:ident) => {
21 #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
22 #[repr(C)]
23 pub struct $struct_name {
24 pub inner: PixelValue,
25 }
26
27 impl ::core::fmt::Debug for $struct_name {
28 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
29 write!(f, "{}", self.inner)
30 }
31 }
32
33 impl PixelValueTaker for $struct_name {
34 fn from_pixel_value(inner: PixelValue) -> Self {
35 Self { inner }
36 }
37 }
38
39 impl_pixel_value!($struct_name);
40 };
41}
42
43define_border_radius_property!(StyleBorderTopLeftRadius);
45define_border_radius_property!(StyleBorderTopRightRadius);
47define_border_radius_property!(StyleBorderBottomLeftRadius);
49define_border_radius_property!(StyleBorderBottomRightRadius);
51
52#[cfg(feature = "parser")]
56#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
57pub struct StyleBorderRadius {
58 pub top_left: PixelValue,
59 pub top_right: PixelValue,
60 pub bottom_left: PixelValue,
61 pub bottom_right: PixelValue,
62}
63
64#[derive(Clone, PartialEq)]
68pub enum CssBorderRadiusParseError<'a> {
69 TooManyValues(&'a str),
71 PixelValue(CssPixelValueParseError<'a>),
73}
74
75impl_debug_as_display!(CssBorderRadiusParseError<'a>);
76impl_display! { CssBorderRadiusParseError<'a>, {
77 TooManyValues(val) => format!("Too many values for border-radius: \"{}\"", val),
78 PixelValue(e) => format!("{}", e),
79}}
80impl_from!(
81 CssPixelValueParseError<'a>,
82 CssBorderRadiusParseError::PixelValue
83);
84
85#[derive(Debug, Clone, PartialEq)]
87#[repr(C, u8)]
88pub enum CssBorderRadiusParseErrorOwned {
89 TooManyValues(AzString),
90 PixelValue(CssPixelValueParseErrorOwned),
91}
92
93#[derive(Debug, Clone, PartialEq)]
95#[repr(C)]
96pub struct CssStyleBorderRadiusParseErrorOwned {
97 pub inner: CssBorderRadiusParseErrorOwned,
98}
99
100impl From<CssBorderRadiusParseErrorOwned> for CssStyleBorderRadiusParseErrorOwned {
101 fn from(v: CssBorderRadiusParseErrorOwned) -> Self {
102 Self { inner: v }
103 }
104}
105
106impl<'a> CssBorderRadiusParseError<'a> {
107 pub fn to_contained(&self) -> CssBorderRadiusParseErrorOwned {
108 match self {
109 CssBorderRadiusParseError::TooManyValues(s) => {
110 CssBorderRadiusParseErrorOwned::TooManyValues(s.to_string().into())
111 }
112 CssBorderRadiusParseError::PixelValue(e) => {
113 CssBorderRadiusParseErrorOwned::PixelValue(e.to_contained())
114 }
115 }
116 }
117}
118
119impl CssBorderRadiusParseErrorOwned {
120 pub fn to_shared<'a>(&'a self) -> CssBorderRadiusParseError<'a> {
121 match self {
122 CssBorderRadiusParseErrorOwned::TooManyValues(s) => {
123 CssBorderRadiusParseError::TooManyValues(s)
124 }
125 CssBorderRadiusParseErrorOwned::PixelValue(e) => {
126 CssBorderRadiusParseError::PixelValue(e.to_shared())
127 }
128 }
129 }
130}
131
132macro_rules! define_border_radius_parse_error {
134 ($error_name:ident, $error_name_owned:ident) => {
135 #[derive(Clone, PartialEq)]
136 pub enum $error_name<'a> {
137 PixelValue(CssPixelValueParseError<'a>),
138 }
139
140 impl_debug_as_display!($error_name<'a>);
141 impl_display! { $error_name<'a>, {
142 PixelValue(e) => format!("{}", e),
143 }}
144
145 impl_from!(CssPixelValueParseError<'a>, $error_name::PixelValue);
146
147 #[derive(Debug, Clone, PartialEq)]
148 #[repr(C, u8)]
149 pub enum $error_name_owned {
150 PixelValue(CssPixelValueParseErrorOwned),
151 }
152
153 impl<'a> $error_name<'a> {
154 pub fn to_contained(&self) -> $error_name_owned {
155 match self {
156 $error_name::PixelValue(e) => $error_name_owned::PixelValue(e.to_contained()),
157 }
158 }
159 }
160
161 impl $error_name_owned {
162 pub fn to_shared<'a>(&'a self) -> $error_name<'a> {
163 match self {
164 $error_name_owned::PixelValue(e) => $error_name::PixelValue(e.to_shared()),
165 }
166 }
167 }
168 };
169}
170
171define_border_radius_parse_error!(
172 StyleBorderTopLeftRadiusParseError,
173 StyleBorderTopLeftRadiusParseErrorOwned
174);
175define_border_radius_parse_error!(
176 StyleBorderTopRightRadiusParseError,
177 StyleBorderTopRightRadiusParseErrorOwned
178);
179define_border_radius_parse_error!(
180 StyleBorderBottomLeftRadiusParseError,
181 StyleBorderBottomLeftRadiusParseErrorOwned
182);
183define_border_radius_parse_error!(
184 StyleBorderBottomRightRadiusParseError,
185 StyleBorderBottomRightRadiusParseErrorOwned
186);
187
188#[cfg(feature = "parser")]
192pub fn parse_style_border_radius<'a>(
193 input: &'a str,
194) -> Result<StyleBorderRadius, CssBorderRadiusParseError<'a>> {
195 let components: Vec<_> = input.split_whitespace().collect();
196 let mut values = Vec::with_capacity(components.len());
197 for comp in components.iter() {
198 values.push(parse_pixel_value(comp)?);
199 }
200
201 match values.len() {
202 1 => Ok(StyleBorderRadius {
203 top_left: values[0],
204 top_right: values[0],
205 bottom_right: values[0],
206 bottom_left: values[0],
207 }),
208 2 => Ok(StyleBorderRadius {
209 top_left: values[0],
210 top_right: values[1],
211 bottom_right: values[0],
212 bottom_left: values[1],
213 }),
214 3 => Ok(StyleBorderRadius {
215 top_left: values[0],
216 top_right: values[1],
217 bottom_right: values[2],
218 bottom_left: values[1],
219 }),
220 4 => Ok(StyleBorderRadius {
221 top_left: values[0],
222 top_right: values[1],
223 bottom_right: values[2],
224 bottom_left: values[3],
225 }),
226 _ => Err(CssBorderRadiusParseError::TooManyValues(input)),
227 }
228}
229
230#[cfg(feature = "parser")]
232pub fn parse_style_border_top_left_radius<'a>(
233 input: &'a str,
234) -> Result<StyleBorderTopLeftRadius, StyleBorderTopLeftRadiusParseError<'a>> {
235 let pixel_value = parse_pixel_value(input)?;
236 Ok(StyleBorderTopLeftRadius { inner: pixel_value })
237}
238
239#[cfg(feature = "parser")]
241pub fn parse_style_border_top_right_radius<'a>(
242 input: &'a str,
243) -> Result<StyleBorderTopRightRadius, StyleBorderTopRightRadiusParseError<'a>> {
244 let pixel_value = parse_pixel_value(input)?;
245 Ok(StyleBorderTopRightRadius { inner: pixel_value })
246}
247
248#[cfg(feature = "parser")]
250pub fn parse_style_border_bottom_left_radius<'a>(
251 input: &'a str,
252) -> Result<StyleBorderBottomLeftRadius, StyleBorderBottomLeftRadiusParseError<'a>> {
253 let pixel_value = parse_pixel_value(input)?;
254 Ok(StyleBorderBottomLeftRadius { inner: pixel_value })
255}
256
257#[cfg(feature = "parser")]
259pub fn parse_style_border_bottom_right_radius<'a>(
260 input: &'a str,
261) -> Result<StyleBorderBottomRightRadius, StyleBorderBottomRightRadiusParseError<'a>> {
262 let pixel_value = parse_pixel_value(input)?;
263 Ok(StyleBorderBottomRightRadius { inner: pixel_value })
264}
265
266#[cfg(all(test, feature = "parser"))]
267mod tests {
268 use super::*;
269
270 #[test]
271 fn test_parse_border_radius_shorthand() {
272 let result = parse_style_border_radius("10px").unwrap();
274 assert_eq!(result.top_left, PixelValue::px(10.0));
275 assert_eq!(result.top_right, PixelValue::px(10.0));
276 assert_eq!(result.bottom_right, PixelValue::px(10.0));
277 assert_eq!(result.bottom_left, PixelValue::px(10.0));
278
279 let result = parse_style_border_radius("10px 5%").unwrap();
281 assert_eq!(result.top_left, PixelValue::px(10.0));
282 assert_eq!(result.top_right, PixelValue::percent(5.0));
283 assert_eq!(result.bottom_right, PixelValue::px(10.0));
284 assert_eq!(result.bottom_left, PixelValue::percent(5.0));
285
286 let result = parse_style_border_radius("2px 4px 8px").unwrap();
288 assert_eq!(result.top_left, PixelValue::px(2.0));
289 assert_eq!(result.top_right, PixelValue::px(4.0));
290 assert_eq!(result.bottom_right, PixelValue::px(8.0));
291 assert_eq!(result.bottom_left, PixelValue::px(4.0));
292
293 let result = parse_style_border_radius("1px 0 3px 4px").unwrap();
295 assert_eq!(result.top_left, PixelValue::px(1.0));
296 assert_eq!(result.top_right, PixelValue::px(0.0));
297 assert_eq!(result.bottom_right, PixelValue::px(3.0));
298 assert_eq!(result.bottom_left, PixelValue::px(4.0));
299
300 let result = parse_style_border_radius(" 1em 2em ").unwrap();
302 assert_eq!(result.top_left, PixelValue::em(1.0));
303 assert_eq!(result.top_right, PixelValue::em(2.0));
304 }
305
306 #[test]
307 fn test_parse_border_radius_shorthand_errors() {
308 assert!(parse_style_border_radius("").is_err());
309 assert!(parse_style_border_radius("1px 2px 3px 4px 5px").is_err());
310 assert!(parse_style_border_radius("1px bad 3px").is_err());
311 }
312
313 #[test]
314 fn test_parse_longhand_radius() {
315 let result = parse_style_border_top_left_radius("25%").unwrap();
316 assert_eq!(result.inner, PixelValue::percent(25.0));
317 }
318}