Skip to main content

azul_css/props/layout/
spacing.rs

1//! CSS properties for `margin` and `padding`.
2
3use alloc::{
4    string::{String, ToString},
5    vec::Vec,
6};
7
8#[cfg(feature = "parser")]
9use crate::props::basic::pixel::{parse_pixel_value_with_auto, PixelValueWithAuto};
10use crate::{
11    css::PrintAsCssValue,
12    props::{
13        basic::pixel::{CssPixelValueParseError, CssPixelValueParseErrorOwned, PixelValue},
14        macros::PixelValueTaker,
15    },
16};
17
18// --- TYPE DEFINITIONS ---
19
20// Spacing properties - wrapper structs around PixelValue for type safety
21
22/// Layout padding top value
23#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
24#[repr(C)]
25pub struct LayoutPaddingTop {
26    pub inner: PixelValue,
27}
28
29impl ::core::fmt::Debug for LayoutPaddingTop {
30    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
31        write!(f, "{}", self.inner)
32    }
33}
34
35impl PixelValueTaker for LayoutPaddingTop {
36    fn from_pixel_value(inner: PixelValue) -> Self {
37        Self { inner }
38    }
39}
40
41impl_pixel_value!(LayoutPaddingTop);
42
43/// Layout padding right value
44#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
45#[repr(C)]
46pub struct LayoutPaddingRight {
47    pub inner: PixelValue,
48}
49
50impl ::core::fmt::Debug for LayoutPaddingRight {
51    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
52        write!(f, "{}", self.inner)
53    }
54}
55
56impl PixelValueTaker for LayoutPaddingRight {
57    fn from_pixel_value(inner: PixelValue) -> Self {
58        Self { inner }
59    }
60}
61
62impl_pixel_value!(LayoutPaddingRight);
63
64/// Layout padding bottom value
65#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
66#[repr(C)]
67pub struct LayoutPaddingBottom {
68    pub inner: PixelValue,
69}
70
71impl ::core::fmt::Debug for LayoutPaddingBottom {
72    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
73        write!(f, "{}", self.inner)
74    }
75}
76
77impl PixelValueTaker for LayoutPaddingBottom {
78    fn from_pixel_value(inner: PixelValue) -> Self {
79        Self { inner }
80    }
81}
82
83impl_pixel_value!(LayoutPaddingBottom);
84
85/// Layout padding left value
86#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
87#[repr(C)]
88pub struct LayoutPaddingLeft {
89    pub inner: PixelValue,
90}
91
92impl ::core::fmt::Debug for LayoutPaddingLeft {
93    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
94        write!(f, "{}", self.inner)
95    }
96}
97
98impl PixelValueTaker for LayoutPaddingLeft {
99    fn from_pixel_value(inner: PixelValue) -> Self {
100        Self { inner }
101    }
102}
103
104impl_pixel_value!(LayoutPaddingLeft);
105
106/// Layout padding inline start value (for RTL/LTR support)
107#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
108#[repr(C)]
109pub struct LayoutPaddingInlineStart {
110    pub inner: PixelValue,
111}
112
113impl ::core::fmt::Debug for LayoutPaddingInlineStart {
114    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
115        write!(f, "{}", self.inner)
116    }
117}
118
119impl PixelValueTaker for LayoutPaddingInlineStart {
120    fn from_pixel_value(inner: PixelValue) -> Self {
121        Self { inner }
122    }
123}
124
125impl_pixel_value!(LayoutPaddingInlineStart);
126
127/// Layout padding inline end value (for RTL/LTR support)
128#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
129#[repr(C)]
130pub struct LayoutPaddingInlineEnd {
131    pub inner: PixelValue,
132}
133
134impl ::core::fmt::Debug for LayoutPaddingInlineEnd {
135    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
136        write!(f, "{}", self.inner)
137    }
138}
139
140impl PixelValueTaker for LayoutPaddingInlineEnd {
141    fn from_pixel_value(inner: PixelValue) -> Self {
142        Self { inner }
143    }
144}
145
146impl_pixel_value!(LayoutPaddingInlineEnd);
147
148/// Layout margin top value
149#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
150#[repr(C)]
151pub struct LayoutMarginTop {
152    pub inner: PixelValue,
153}
154
155impl ::core::fmt::Debug for LayoutMarginTop {
156    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
157        write!(f, "{}", self.inner)
158    }
159}
160
161impl PixelValueTaker for LayoutMarginTop {
162    fn from_pixel_value(inner: PixelValue) -> Self {
163        Self { inner }
164    }
165}
166
167impl_pixel_value!(LayoutMarginTop);
168
169/// Layout margin right value
170#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
171#[repr(C)]
172pub struct LayoutMarginRight {
173    pub inner: PixelValue,
174}
175
176impl ::core::fmt::Debug for LayoutMarginRight {
177    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
178        write!(f, "{}", self.inner)
179    }
180}
181
182impl PixelValueTaker for LayoutMarginRight {
183    fn from_pixel_value(inner: PixelValue) -> Self {
184        Self { inner }
185    }
186}
187
188impl_pixel_value!(LayoutMarginRight);
189
190/// Layout margin bottom value
191#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
192#[repr(C)]
193pub struct LayoutMarginBottom {
194    pub inner: PixelValue,
195}
196
197impl ::core::fmt::Debug for LayoutMarginBottom {
198    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
199        write!(f, "{}", self.inner)
200    }
201}
202
203impl PixelValueTaker for LayoutMarginBottom {
204    fn from_pixel_value(inner: PixelValue) -> Self {
205        Self { inner }
206    }
207}
208
209impl_pixel_value!(LayoutMarginBottom);
210
211/// Layout margin left value
212#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
213#[repr(C)]
214pub struct LayoutMarginLeft {
215    pub inner: PixelValue,
216}
217
218impl ::core::fmt::Debug for LayoutMarginLeft {
219    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
220        write!(f, "{}", self.inner)
221    }
222}
223
224impl PixelValueTaker for LayoutMarginLeft {
225    fn from_pixel_value(inner: PixelValue) -> Self {
226        Self { inner }
227    }
228}
229
230impl_pixel_value!(LayoutMarginLeft);
231
232/// Layout column gap value (for flexbox/grid)
233#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
234#[repr(C)]
235pub struct LayoutColumnGap {
236    pub inner: PixelValue,
237}
238
239impl ::core::fmt::Debug for LayoutColumnGap {
240    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
241        write!(f, "{}", self.inner)
242    }
243}
244
245impl PixelValueTaker for LayoutColumnGap {
246    fn from_pixel_value(inner: PixelValue) -> Self {
247        Self { inner }
248    }
249}
250
251impl_pixel_value!(LayoutColumnGap);
252
253/// Layout row gap value (for flexbox/grid)
254#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
255#[repr(C)]
256pub struct LayoutRowGap {
257    pub inner: PixelValue,
258}
259
260impl ::core::fmt::Debug for LayoutRowGap {
261    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
262        write!(f, "{}", self.inner)
263    }
264}
265
266impl PixelValueTaker for LayoutRowGap {
267    fn from_pixel_value(inner: PixelValue) -> Self {
268        Self { inner }
269    }
270}
271
272impl_pixel_value!(LayoutRowGap);
273
274// --- PARSERS ---
275
276// -- Padding Shorthand Parser --
277
278#[cfg(feature = "parser")]
279#[derive(Clone, PartialEq)]
280pub enum LayoutPaddingParseError<'a> {
281    PixelValueParseError(CssPixelValueParseError<'a>),
282    TooManyValues,
283    TooFewValues,
284}
285
286#[cfg(feature = "parser")]
287impl_debug_as_display!(LayoutPaddingParseError<'a>);
288
289#[cfg(feature = "parser")]
290impl_display! { LayoutPaddingParseError<'a>, {
291    PixelValueParseError(e) => format!("Could not parse pixel value: {}", e),
292    TooManyValues => "Too many values: padding property accepts at most 4 values.",
293    TooFewValues => "Too few values: padding property requires at least 1 value.",
294}}
295
296#[cfg(feature = "parser")]
297impl_from!(
298    CssPixelValueParseError<'a>,
299    LayoutPaddingParseError::PixelValueParseError
300);
301
302#[cfg(feature = "parser")]
303#[derive(Debug, Clone, PartialEq)]
304pub enum LayoutPaddingParseErrorOwned {
305    PixelValueParseError(CssPixelValueParseErrorOwned),
306    TooManyValues,
307    TooFewValues,
308}
309
310#[cfg(feature = "parser")]
311impl<'a> LayoutPaddingParseError<'a> {
312    pub fn to_contained(&self) -> LayoutPaddingParseErrorOwned {
313        match self {
314            LayoutPaddingParseError::PixelValueParseError(e) => {
315                LayoutPaddingParseErrorOwned::PixelValueParseError(e.to_contained())
316            }
317            LayoutPaddingParseError::TooManyValues => LayoutPaddingParseErrorOwned::TooManyValues,
318            LayoutPaddingParseError::TooFewValues => LayoutPaddingParseErrorOwned::TooFewValues,
319        }
320    }
321}
322
323#[cfg(feature = "parser")]
324impl LayoutPaddingParseErrorOwned {
325    pub fn to_shared<'a>(&'a self) -> LayoutPaddingParseError<'a> {
326        match self {
327            LayoutPaddingParseErrorOwned::PixelValueParseError(e) => {
328                LayoutPaddingParseError::PixelValueParseError(e.to_shared())
329            }
330            LayoutPaddingParseErrorOwned::TooManyValues => LayoutPaddingParseError::TooManyValues,
331            LayoutPaddingParseErrorOwned::TooFewValues => LayoutPaddingParseError::TooFewValues,
332        }
333    }
334}
335
336#[cfg(feature = "parser")]
337#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
338pub struct LayoutPadding {
339    pub top: PixelValueWithAuto,
340    pub bottom: PixelValueWithAuto,
341    pub left: PixelValueWithAuto,
342    pub right: PixelValueWithAuto,
343}
344
345#[cfg(feature = "parser")]
346pub fn parse_layout_padding<'a>(
347    input: &'a str,
348) -> Result<LayoutPadding, LayoutPaddingParseError<'a>> {
349    let values: Vec<_> = input.split_whitespace().collect();
350
351    let parsed_values: Vec<PixelValueWithAuto> = values
352        .iter()
353        .map(|s| parse_pixel_value_with_auto(s))
354        .collect::<Result<_, _>>()?;
355
356    match parsed_values.len() {
357        1 => {
358            // top, right, bottom, left
359            let all = parsed_values[0];
360            Ok(LayoutPadding {
361                top: all,
362                right: all,
363                bottom: all,
364                left: all,
365            })
366        }
367        2 => {
368            // top/bottom, left/right
369            let vertical = parsed_values[0];
370            let horizontal = parsed_values[1];
371            Ok(LayoutPadding {
372                top: vertical,
373                right: horizontal,
374                bottom: vertical,
375                left: horizontal,
376            })
377        }
378        3 => {
379            // top, left/right, bottom
380            let top = parsed_values[0];
381            let horizontal = parsed_values[1];
382            let bottom = parsed_values[2];
383            Ok(LayoutPadding {
384                top,
385                right: horizontal,
386                bottom,
387                left: horizontal,
388            })
389        }
390        4 => {
391            // top, right, bottom, left
392            Ok(LayoutPadding {
393                top: parsed_values[0],
394                right: parsed_values[1],
395                bottom: parsed_values[2],
396                left: parsed_values[3],
397            })
398        }
399        0 => Err(LayoutPaddingParseError::TooFewValues),
400        _ => Err(LayoutPaddingParseError::TooManyValues),
401    }
402}
403
404// -- Margin Shorthand Parser --
405
406#[cfg(feature = "parser")]
407#[derive(Clone, PartialEq)]
408pub enum LayoutMarginParseError<'a> {
409    PixelValueParseError(CssPixelValueParseError<'a>),
410    TooManyValues,
411    TooFewValues,
412}
413
414#[cfg(feature = "parser")]
415impl_debug_as_display!(LayoutMarginParseError<'a>);
416
417#[cfg(feature = "parser")]
418impl_display! { LayoutMarginParseError<'a>, {
419    PixelValueParseError(e) => format!("Could not parse pixel value: {}", e),
420    TooManyValues => "Too many values: margin property accepts at most 4 values.",
421    TooFewValues => "Too few values: margin property requires at least 1 value.",
422}}
423
424#[cfg(feature = "parser")]
425impl_from!(
426    CssPixelValueParseError<'a>,
427    LayoutMarginParseError::PixelValueParseError
428);
429
430#[cfg(feature = "parser")]
431#[derive(Debug, Clone, PartialEq)]
432pub enum LayoutMarginParseErrorOwned {
433    PixelValueParseError(CssPixelValueParseErrorOwned),
434    TooManyValues,
435    TooFewValues,
436}
437
438#[cfg(feature = "parser")]
439impl<'a> LayoutMarginParseError<'a> {
440    pub fn to_contained(&self) -> LayoutMarginParseErrorOwned {
441        match self {
442            LayoutMarginParseError::PixelValueParseError(e) => {
443                LayoutMarginParseErrorOwned::PixelValueParseError(e.to_contained())
444            }
445            LayoutMarginParseError::TooManyValues => LayoutMarginParseErrorOwned::TooManyValues,
446            LayoutMarginParseError::TooFewValues => LayoutMarginParseErrorOwned::TooFewValues,
447        }
448    }
449}
450
451#[cfg(feature = "parser")]
452impl LayoutMarginParseErrorOwned {
453    pub fn to_shared<'a>(&'a self) -> LayoutMarginParseError<'a> {
454        match self {
455            LayoutMarginParseErrorOwned::PixelValueParseError(e) => {
456                LayoutMarginParseError::PixelValueParseError(e.to_shared())
457            }
458            LayoutMarginParseErrorOwned::TooManyValues => LayoutMarginParseError::TooManyValues,
459            LayoutMarginParseErrorOwned::TooFewValues => LayoutMarginParseError::TooFewValues,
460        }
461    }
462}
463
464#[cfg(feature = "parser")]
465#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
466pub struct LayoutMargin {
467    pub top: PixelValueWithAuto,
468    pub bottom: PixelValueWithAuto,
469    pub left: PixelValueWithAuto,
470    pub right: PixelValueWithAuto,
471}
472
473#[cfg(feature = "parser")]
474pub fn parse_layout_margin<'a>(input: &'a str) -> Result<LayoutMargin, LayoutMarginParseError<'a>> {
475    // Margin parsing logic is identical to padding, so we can reuse the padding parser
476    // and just map the Ok and Err variants to the margin-specific types.
477    match parse_layout_padding(input) {
478        Ok(padding) => Ok(LayoutMargin {
479            top: padding.top,
480            left: padding.left,
481            right: padding.right,
482            bottom: padding.bottom,
483        }),
484        Err(e) => match e {
485            LayoutPaddingParseError::PixelValueParseError(err) => {
486                Err(LayoutMarginParseError::PixelValueParseError(err))
487            }
488            LayoutPaddingParseError::TooManyValues => Err(LayoutMarginParseError::TooManyValues),
489            LayoutPaddingParseError::TooFewValues => Err(LayoutMarginParseError::TooFewValues),
490        },
491    }
492}
493
494// -- Longhand Property Parsers --
495
496macro_rules! typed_pixel_value_parser {
497    (
498        $fn:ident, $fn_str:expr, $return:ident, $return_str:expr, $import_str:expr, $test_str:expr
499    ) => {
500        ///Parses a `
501        #[doc = $return_str]
502        ///` attribute from a `&str`
503        ///
504        ///# Example
505        ///
506        ///```rust
507        #[doc = $import_str]
508        #[doc = $test_str]
509        ///```
510        pub fn $fn<'a>(input: &'a str) -> Result<$return, CssPixelValueParseError<'a>> {
511            crate::props::basic::parse_pixel_value(input).and_then(|e| Ok($return { inner: e }))
512        }
513
514        impl crate::props::formatter::FormatAsCssValue for $return {
515            fn format_as_css_value(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
516                self.inner.format_as_css_value(f)
517            }
518        }
519    };
520    ($fn:ident, $return:ident) => {
521        typed_pixel_value_parser!(
522            $fn,
523            stringify!($fn),
524            $return,
525            stringify!($return),
526            concat!(
527                "# extern crate azul_css;",
528                "\r\n",
529                "# use azul_css::props::layout::spacing::",
530                stringify!($fn),
531                ";",
532                "\r\n",
533                "# use azul_css::props::basic::pixel::PixelValue;\r\n",
534                "# use azul_css::props::layout::spacing::",
535                stringify!($return),
536                ";\r\n"
537            ),
538            concat!(
539                "assert_eq!(",
540                stringify!($fn),
541                "(\"5px\"), Ok(",
542                stringify!($return),
543                " { inner: PixelValue::px(5.0) }));"
544            )
545        );
546    };
547}
548
549#[cfg(feature = "parser")]
550typed_pixel_value_parser!(parse_layout_padding_top, LayoutPaddingTop);
551#[cfg(feature = "parser")]
552typed_pixel_value_parser!(parse_layout_padding_right, LayoutPaddingRight);
553#[cfg(feature = "parser")]
554typed_pixel_value_parser!(parse_layout_padding_bottom, LayoutPaddingBottom);
555#[cfg(feature = "parser")]
556typed_pixel_value_parser!(parse_layout_padding_left, LayoutPaddingLeft);
557#[cfg(feature = "parser")]
558typed_pixel_value_parser!(parse_layout_padding_inline_start, LayoutPaddingInlineStart);
559#[cfg(feature = "parser")]
560typed_pixel_value_parser!(parse_layout_padding_inline_end, LayoutPaddingInlineEnd);
561
562#[cfg(feature = "parser")]
563typed_pixel_value_parser!(parse_layout_margin_top, LayoutMarginTop);
564#[cfg(feature = "parser")]
565typed_pixel_value_parser!(parse_layout_margin_right, LayoutMarginRight);
566#[cfg(feature = "parser")]
567typed_pixel_value_parser!(parse_layout_margin_bottom, LayoutMarginBottom);
568#[cfg(feature = "parser")]
569typed_pixel_value_parser!(parse_layout_margin_left, LayoutMarginLeft);
570
571#[cfg(feature = "parser")]
572typed_pixel_value_parser!(parse_layout_column_gap, LayoutColumnGap);
573#[cfg(feature = "parser")]
574typed_pixel_value_parser!(parse_layout_row_gap, LayoutRowGap);
575
576#[cfg(all(test, feature = "parser"))]
577mod tests {
578    use super::*;
579    use crate::props::basic::pixel::{PixelValue, PixelValueWithAuto};
580
581    #[test]
582    fn test_parse_layout_padding_shorthand() {
583        // 1 value
584        let result = parse_layout_padding("10px").unwrap();
585        assert_eq!(result.top, PixelValueWithAuto::Exact(PixelValue::px(10.0)));
586        assert_eq!(
587            result.right,
588            PixelValueWithAuto::Exact(PixelValue::px(10.0))
589        );
590        assert_eq!(
591            result.bottom,
592            PixelValueWithAuto::Exact(PixelValue::px(10.0))
593        );
594        assert_eq!(result.left, PixelValueWithAuto::Exact(PixelValue::px(10.0)));
595
596        // 2 values
597        let result = parse_layout_padding("5% 2em").unwrap();
598        assert_eq!(
599            result.top,
600            PixelValueWithAuto::Exact(PixelValue::percent(5.0))
601        );
602        assert_eq!(result.right, PixelValueWithAuto::Exact(PixelValue::em(2.0)));
603        assert_eq!(
604            result.bottom,
605            PixelValueWithAuto::Exact(PixelValue::percent(5.0))
606        );
607        assert_eq!(result.left, PixelValueWithAuto::Exact(PixelValue::em(2.0)));
608
609        // 3 values
610        let result = parse_layout_padding("1px 2px 3px").unwrap();
611        assert_eq!(result.top, PixelValueWithAuto::Exact(PixelValue::px(1.0)));
612        assert_eq!(result.right, PixelValueWithAuto::Exact(PixelValue::px(2.0)));
613        assert_eq!(
614            result.bottom,
615            PixelValueWithAuto::Exact(PixelValue::px(3.0))
616        );
617        assert_eq!(result.left, PixelValueWithAuto::Exact(PixelValue::px(2.0)));
618
619        // 4 values
620        let result = parse_layout_padding("1px 2px 3px 4px").unwrap();
621        assert_eq!(result.top, PixelValueWithAuto::Exact(PixelValue::px(1.0)));
622        assert_eq!(result.right, PixelValueWithAuto::Exact(PixelValue::px(2.0)));
623        assert_eq!(
624            result.bottom,
625            PixelValueWithAuto::Exact(PixelValue::px(3.0))
626        );
627        assert_eq!(result.left, PixelValueWithAuto::Exact(PixelValue::px(4.0)));
628
629        // Whitespace
630        let result = parse_layout_padding("  1px   2px  ").unwrap();
631        assert_eq!(result.top, PixelValueWithAuto::Exact(PixelValue::px(1.0)));
632        assert_eq!(result.right, PixelValueWithAuto::Exact(PixelValue::px(2.0)));
633    }
634
635    #[test]
636    fn test_parse_layout_padding_errors() {
637        assert!(matches!(
638            parse_layout_padding("").err().unwrap(),
639            LayoutPaddingParseError::TooFewValues
640        ));
641        assert!(matches!(
642            parse_layout_padding("1px 2px 3px 4px 5px").err().unwrap(),
643            LayoutPaddingParseError::TooManyValues
644        ));
645        assert!(matches!(
646            parse_layout_padding("1px oops 3px").err().unwrap(),
647            LayoutPaddingParseError::PixelValueParseError(_)
648        ));
649    }
650
651    #[test]
652    fn test_parse_layout_margin_shorthand() {
653        // 1 value with auto
654        let result = parse_layout_margin("auto").unwrap();
655        assert_eq!(result.top, PixelValueWithAuto::Auto);
656        assert_eq!(result.right, PixelValueWithAuto::Auto);
657        assert_eq!(result.bottom, PixelValueWithAuto::Auto);
658        assert_eq!(result.left, PixelValueWithAuto::Auto);
659
660        // 2 values
661        let result = parse_layout_margin("10px auto").unwrap();
662        assert_eq!(result.top, PixelValueWithAuto::Exact(PixelValue::px(10.0)));
663        assert_eq!(result.right, PixelValueWithAuto::Auto);
664        assert_eq!(
665            result.bottom,
666            PixelValueWithAuto::Exact(PixelValue::px(10.0))
667        );
668        assert_eq!(result.left, PixelValueWithAuto::Auto);
669    }
670
671    #[test]
672    fn test_parse_layout_margin_errors() {
673        assert!(matches!(
674            parse_layout_margin("").err().unwrap(),
675            LayoutMarginParseError::TooFewValues
676        ));
677        assert!(matches!(
678            parse_layout_margin("1px 2px 3px 4px 5px").err().unwrap(),
679            LayoutMarginParseError::TooManyValues
680        ));
681        assert!(matches!(
682            parse_layout_margin("1px invalid").err().unwrap(),
683            LayoutMarginParseError::PixelValueParseError(_)
684        ));
685    }
686
687    #[test]
688    fn test_parse_longhand_spacing() {
689        assert_eq!(
690            parse_layout_padding_left("2em").unwrap(),
691            LayoutPaddingLeft {
692                inner: PixelValue::em(2.0)
693            }
694        );
695        assert!(parse_layout_margin_top("auto").is_err()); // Longhands don't parse "auto"
696        assert_eq!(
697            parse_layout_column_gap("20px").unwrap(),
698            LayoutColumnGap {
699                inner: PixelValue::px(20.0)
700            }
701        );
702    }
703}