1use alloc::string::{String, ToString};
6use crate::corety::AzString;
7
8#[cfg(feature = "parser")]
9use crate::props::basic::pixel::parse_pixel_value;
10use crate::props::{
11 basic::pixel::{CssPixelValueParseError, CssPixelValueParseErrorOwned, PixelValue},
12 formatter::PrintAsCssValue,
13 macros::PixelValueTaker,
14};
15
16#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
20#[repr(C)]
21#[derive(Default)]
22pub enum LayoutPosition {
23 #[default]
24 Static,
25 Relative,
26 Absolute,
27 Fixed,
28 Sticky,
29}
30
31impl LayoutPosition {
32 pub fn is_positioned(&self) -> bool {
33 *self != LayoutPosition::Static
34 }
35}
36
37
38impl PrintAsCssValue for LayoutPosition {
39 fn print_as_css_value(&self) -> String {
40 String::from(match self {
41 LayoutPosition::Static => "static",
42 LayoutPosition::Relative => "relative",
43 LayoutPosition::Absolute => "absolute",
44 LayoutPosition::Fixed => "fixed",
45 LayoutPosition::Sticky => "sticky",
46 })
47 }
48}
49
50impl_enum_fmt!(LayoutPosition, Static, Fixed, Absolute, Relative, Sticky);
51
52#[derive(Clone, PartialEq)]
55pub enum LayoutPositionParseError<'a> {
56 InvalidValue(&'a str),
57}
58
59impl_debug_as_display!(LayoutPositionParseError<'a>);
60impl_display! { LayoutPositionParseError<'a>, {
61 InvalidValue(val) => format!("Invalid position value: \"{}\"", val),
62}}
63
64#[derive(Debug, Clone, PartialEq)]
65#[repr(C, u8)]
66pub enum LayoutPositionParseErrorOwned {
67 InvalidValue(AzString),
68}
69
70impl<'a> LayoutPositionParseError<'a> {
71 pub fn to_contained(&self) -> LayoutPositionParseErrorOwned {
72 match self {
73 LayoutPositionParseError::InvalidValue(s) => {
74 LayoutPositionParseErrorOwned::InvalidValue(s.to_string().into())
75 }
76 }
77 }
78}
79
80impl LayoutPositionParseErrorOwned {
81 pub fn to_shared<'a>(&'a self) -> LayoutPositionParseError<'a> {
82 match self {
83 LayoutPositionParseErrorOwned::InvalidValue(s) => {
84 LayoutPositionParseError::InvalidValue(s.as_str())
85 }
86 }
87 }
88}
89
90#[cfg(feature = "parser")]
91pub fn parse_layout_position<'a>(
92 input: &'a str,
93) -> Result<LayoutPosition, LayoutPositionParseError<'a>> {
94 let input = input.trim();
95 match input {
96 "static" => Ok(LayoutPosition::Static),
97 "relative" => Ok(LayoutPosition::Relative),
98 "absolute" => Ok(LayoutPosition::Absolute),
99 "fixed" => Ok(LayoutPosition::Fixed),
100 "sticky" => Ok(LayoutPosition::Sticky),
101 _ => Err(LayoutPositionParseError::InvalidValue(input)),
102 }
103}
104
105macro_rules! define_position_property {
108 ($struct_name:ident) => {
109 #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
110 #[repr(C)]
111 pub struct $struct_name {
112 pub inner: PixelValue,
113 }
114
115 impl ::core::fmt::Debug for $struct_name {
116 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
117 write!(f, "{}", self.inner)
118 }
119 }
120
121 impl PixelValueTaker for $struct_name {
122 fn from_pixel_value(inner: PixelValue) -> Self {
123 Self { inner }
124 }
125 }
126
127 impl_pixel_value!($struct_name);
128
129 impl PrintAsCssValue for $struct_name {
130 fn print_as_css_value(&self) -> String {
131 format!("{}", self.inner)
132 }
133 }
134 };
135}
136
137define_position_property!(LayoutTop);
139define_position_property!(LayoutRight);
141define_position_property!(LayoutInsetBottom);
143define_position_property!(LayoutLeft);
145
146macro_rules! define_offset_parse_error {
149 ($struct_name:ident, $error_name:ident, $error_owned_name:ident, $parse_fn:ident) => {
150 #[derive(Clone, PartialEq)]
151 pub enum $error_name<'a> {
152 PixelValue(CssPixelValueParseError<'a>),
153 }
154 impl_debug_as_display!($error_name<'a>);
155 impl_display! { $error_name<'a>, { PixelValue(e) => format!("{}", e), }}
156 impl_from!(CssPixelValueParseError<'a>, $error_name::PixelValue);
157
158 #[derive(Debug, Clone, PartialEq)]
159 #[repr(C, u8)]
160 pub enum $error_owned_name {
161 PixelValue(CssPixelValueParseErrorOwned),
162 }
163 impl<'a> $error_name<'a> {
164 pub fn to_contained(&self) -> $error_owned_name {
165 match self {
166 $error_name::PixelValue(e) => {
167 $error_owned_name::PixelValue(e.to_contained())
168 }
169 }
170 }
171 }
172 impl $error_owned_name {
173 pub fn to_shared<'a>(&'a self) -> $error_name<'a> {
174 match self {
175 $error_owned_name::PixelValue(e) => {
176 $error_name::PixelValue(e.to_shared())
177 }
178 }
179 }
180 }
181
182 #[cfg(feature = "parser")]
183 pub fn $parse_fn<'a>(input: &'a str) -> Result<$struct_name, $error_name<'a>> {
184 parse_pixel_value(input)
185 .map(|v| $struct_name { inner: v })
186 .map_err(Into::into)
187 }
188 };
189}
190
191define_offset_parse_error!(LayoutTop, LayoutTopParseError, LayoutTopParseErrorOwned, parse_layout_top);
192define_offset_parse_error!(LayoutRight, LayoutRightParseError, LayoutRightParseErrorOwned, parse_layout_right);
193define_offset_parse_error!(LayoutInsetBottom, LayoutInsetBottomParseError, LayoutInsetBottomParseErrorOwned, parse_layout_bottom);
194define_offset_parse_error!(LayoutLeft, LayoutLeftParseError, LayoutLeftParseErrorOwned, parse_layout_left);
195
196#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
200#[repr(C, u8)]
201#[derive(Default)]
202pub enum LayoutZIndex {
203 #[default]
204 Auto,
205 Integer(i32),
206}
207
208impl crate::format_rust_code::FormatAsRustCode for LayoutZIndex {
210 fn format_as_rust_code(&self, _tabs: usize) -> String {
211 match self {
212 LayoutZIndex::Auto => String::from("LayoutZIndex::Auto"),
213 LayoutZIndex::Integer(val) => {
214 format!("LayoutZIndex::Integer({})", val)
215 }
216 }
217 }
218}
219
220
221impl PrintAsCssValue for LayoutZIndex {
222 fn print_as_css_value(&self) -> String {
223 match self {
224 LayoutZIndex::Auto => String::from("auto"),
225 LayoutZIndex::Integer(val) => val.to_string(),
226 }
227 }
228}
229
230#[derive(Clone, PartialEq)]
233pub enum LayoutZIndexParseError<'a> {
234 InvalidValue(&'a str),
235 ParseInt(::core::num::ParseIntError, &'a str),
236}
237impl_debug_as_display!(LayoutZIndexParseError<'a>);
238impl_display! { LayoutZIndexParseError<'a>, {
239 InvalidValue(val) => format!("Invalid z-index value: \"{}\"", val),
240 ParseInt(e, s) => format!("Invalid z-index integer \"{}\": {}", s, e),
241}}
242
243#[derive(Debug, Clone, PartialEq)]
246#[repr(C)]
247pub struct ParseIntErrorWithInput {
248 pub error: AzString,
250 pub input: AzString,
252}
253
254#[derive(Debug, Clone, PartialEq)]
255#[repr(C, u8)]
256pub enum LayoutZIndexParseErrorOwned {
257 InvalidValue(AzString),
258 ParseInt(ParseIntErrorWithInput),
259}
260
261impl<'a> LayoutZIndexParseError<'a> {
262 pub fn to_contained(&self) -> LayoutZIndexParseErrorOwned {
263 match self {
264 LayoutZIndexParseError::InvalidValue(s) => {
265 LayoutZIndexParseErrorOwned::InvalidValue(s.to_string().into())
266 }
267 LayoutZIndexParseError::ParseInt(e, s) => {
268 LayoutZIndexParseErrorOwned::ParseInt(ParseIntErrorWithInput { error: e.to_string().into(), input: s.to_string().into() })
269 }
270 }
271 }
272}
273
274impl LayoutZIndexParseErrorOwned {
275 pub fn to_shared<'a>(&'a self) -> LayoutZIndexParseError<'a> {
281 match self {
282 LayoutZIndexParseErrorOwned::InvalidValue(s) => {
283 LayoutZIndexParseError::InvalidValue(s.as_str())
284 }
285 LayoutZIndexParseErrorOwned::ParseInt(e) => {
286 LayoutZIndexParseError::InvalidValue(e.input.as_str())
288 }
289 }
290 }
291}
292
293#[cfg(feature = "parser")]
294pub fn parse_layout_z_index<'a>(
295 input: &'a str,
296) -> Result<LayoutZIndex, LayoutZIndexParseError<'a>> {
297 let input = input.trim();
298 if input == "auto" {
299 return Ok(LayoutZIndex::Auto);
300 }
301
302 match input.parse::<i32>() {
303 Ok(val) => Ok(LayoutZIndex::Integer(val)),
304 Err(e) => Err(LayoutZIndexParseError::ParseInt(e, input)),
305 }
306}
307
308#[cfg(all(test, feature = "parser"))]
309mod tests {
310 use super::*;
311
312 #[test]
313 fn test_parse_layout_position() {
314 assert_eq!(
315 parse_layout_position("static").unwrap(),
316 LayoutPosition::Static
317 );
318 assert_eq!(
319 parse_layout_position("relative").unwrap(),
320 LayoutPosition::Relative
321 );
322 assert_eq!(
323 parse_layout_position("absolute").unwrap(),
324 LayoutPosition::Absolute
325 );
326 assert_eq!(
327 parse_layout_position("fixed").unwrap(),
328 LayoutPosition::Fixed
329 );
330 assert_eq!(
331 parse_layout_position("sticky").unwrap(),
332 LayoutPosition::Sticky
333 );
334 }
335
336 #[test]
337 fn test_parse_layout_position_whitespace() {
338 assert_eq!(
339 parse_layout_position(" absolute ").unwrap(),
340 LayoutPosition::Absolute
341 );
342 }
343
344 #[test]
345 fn test_parse_layout_position_invalid() {
346 assert!(parse_layout_position("").is_err());
347 assert!(parse_layout_position("absolutely").is_err());
348 }
349
350 #[test]
351 fn test_parse_layout_z_index() {
352 assert_eq!(parse_layout_z_index("auto").unwrap(), LayoutZIndex::Auto);
353 assert_eq!(
354 parse_layout_z_index("10").unwrap(),
355 LayoutZIndex::Integer(10)
356 );
357 assert_eq!(parse_layout_z_index("0").unwrap(), LayoutZIndex::Integer(0));
358 assert_eq!(
359 parse_layout_z_index("-5").unwrap(),
360 LayoutZIndex::Integer(-5)
361 );
362 assert_eq!(
363 parse_layout_z_index(" 999 ").unwrap(),
364 LayoutZIndex::Integer(999)
365 );
366 }
367
368 #[test]
369 fn test_parse_layout_z_index_invalid() {
370 assert!(parse_layout_z_index("10px").is_err());
371 assert!(parse_layout_z_index("1.5").is_err());
372 assert!(parse_layout_z_index("none").is_err());
373 assert!(parse_layout_z_index("").is_err());
374 }
375
376 #[test]
377 fn test_parse_offsets() {
378 assert_eq!(
379 parse_layout_top("10px").unwrap(),
380 LayoutTop {
381 inner: PixelValue::px(10.0)
382 }
383 );
384 assert_eq!(
385 parse_layout_right("5%").unwrap(),
386 LayoutRight {
387 inner: PixelValue::percent(5.0)
388 }
389 );
390 assert_eq!(
391 parse_layout_bottom("2.5em").unwrap(),
392 LayoutInsetBottom {
393 inner: PixelValue::em(2.5)
394 }
395 );
396 assert_eq!(
397 parse_layout_left("0").unwrap(),
398 LayoutLeft {
399 inner: PixelValue::px(0.0)
400 }
401 );
402 }
403
404 #[test]
405 fn test_parse_offsets_invalid() {
406 assert!(parse_layout_top("auto").is_err());
408 assert!(parse_layout_right("").is_err());
409 assert!(parse_layout_bottom("10 px").is_ok());
411 assert!(parse_layout_left("ten pixels").is_err());
412 }
413}