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