Skip to main content

azul_css/props/layout/
flex.rs

1//! CSS properties for flexbox layout.
2
3use alloc::string::{String, ToString};
4use core::num::ParseFloatError;
5
6use crate::{
7    format_rust_code::FormatAsRustCode,
8    props::{
9        basic::length::{parse_float_value, FloatValue},
10        formatter::PrintAsCssValue,
11    },
12};
13
14// --- flex-grow ---
15
16/// Represents a `flex-grow` attribute, which dictates what proportion of the
17/// remaining space in the flex container should be assigned to the item.
18/// Default: 0
19#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
20#[repr(C)]
21pub struct LayoutFlexGrow {
22    pub inner: FloatValue,
23}
24
25impl core::fmt::Debug for LayoutFlexGrow {
26    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
27        write!(f, "{}", self.inner.get())
28    }
29}
30
31impl Default for LayoutFlexGrow {
32    fn default() -> Self {
33        Self {
34            inner: FloatValue::const_new(0),
35        }
36    }
37}
38
39impl PrintAsCssValue for LayoutFlexGrow {
40    fn print_as_css_value(&self) -> String {
41        format!("{}", self.inner)
42    }
43}
44
45impl LayoutFlexGrow {
46    pub fn new(value: isize) -> Self {
47        Self {
48            inner: FloatValue::new(value as f32),
49        }
50    }
51
52    pub const fn const_new(value: isize) -> Self {
53        Self {
54            inner: FloatValue::const_new(value),
55        }
56    }
57
58    pub fn interpolate(&self, other: &Self, t: f32) -> Self {
59        Self {
60            inner: self.inner.interpolate(&other.inner, t),
61        }
62    }
63}
64
65#[cfg(feature = "parser")]
66#[derive(Clone, PartialEq)]
67pub enum FlexGrowParseError<'a> {
68    ParseFloat(ParseFloatError, &'a str),
69    NegativeValue(&'a str),
70}
71
72#[cfg(feature = "parser")]
73impl_debug_as_display!(FlexGrowParseError<'a>);
74#[cfg(feature = "parser")]
75impl_display! { FlexGrowParseError<'a>, {
76    ParseFloat(e, s) => format!("Invalid flex-grow value: \"{}\". Reason: {}", s, e),
77    NegativeValue(s) => format!("Invalid flex-grow value: \"{}\". Flex-grow cannot be negative", s),
78}}
79
80#[cfg(feature = "parser")]
81#[derive(Debug, Clone, PartialEq)]
82pub enum FlexGrowParseErrorOwned {
83    ParseFloat(ParseFloatError, String),
84    NegativeValue(String),
85}
86
87#[cfg(feature = "parser")]
88impl<'a> FlexGrowParseError<'a> {
89    pub fn to_contained(&self) -> FlexGrowParseErrorOwned {
90        match self {
91            FlexGrowParseError::ParseFloat(e, s) => {
92                FlexGrowParseErrorOwned::ParseFloat(e.clone(), s.to_string())
93            }
94            FlexGrowParseError::NegativeValue(s) => {
95                FlexGrowParseErrorOwned::NegativeValue(s.to_string())
96            }
97        }
98    }
99}
100
101#[cfg(feature = "parser")]
102impl FlexGrowParseErrorOwned {
103    pub fn to_shared<'a>(&'a self) -> FlexGrowParseError<'a> {
104        match self {
105            FlexGrowParseErrorOwned::ParseFloat(e, s) => {
106                FlexGrowParseError::ParseFloat(e.clone(), s.as_str())
107            }
108            FlexGrowParseErrorOwned::NegativeValue(s) => {
109                FlexGrowParseError::NegativeValue(s.as_str())
110            }
111        }
112    }
113}
114
115#[cfg(feature = "parser")]
116pub fn parse_layout_flex_grow<'a>(
117    input: &'a str,
118) -> Result<LayoutFlexGrow, FlexGrowParseError<'a>> {
119    match parse_float_value(input) {
120        Ok(o) => {
121            if o.get() < 0.0 {
122                Err(FlexGrowParseError::NegativeValue(input))
123            } else {
124                Ok(LayoutFlexGrow { inner: o })
125            }
126        }
127        Err(e) => Err(FlexGrowParseError::ParseFloat(e, input)),
128    }
129}
130
131// --- flex-shrink ---
132
133/// Represents a `flex-shrink` attribute, which dictates what proportion of
134/// the negative space in the flex container should be removed from the item.
135/// Default: 1
136#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
137#[repr(C)]
138pub struct LayoutFlexShrink {
139    pub inner: FloatValue,
140}
141
142impl core::fmt::Debug for LayoutFlexShrink {
143    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
144        write!(f, "{}", self.inner.get())
145    }
146}
147
148impl Default for LayoutFlexShrink {
149    fn default() -> Self {
150        Self {
151            inner: FloatValue::const_new(1),
152        }
153    }
154}
155
156impl PrintAsCssValue for LayoutFlexShrink {
157    fn print_as_css_value(&self) -> String {
158        format!("{}", self.inner)
159    }
160}
161
162impl LayoutFlexShrink {
163    pub fn interpolate(&self, other: &Self, t: f32) -> Self {
164        Self {
165            inner: self.inner.interpolate(&other.inner, t),
166        }
167    }
168}
169
170#[cfg(feature = "parser")]
171#[derive(Clone, PartialEq)]
172pub enum FlexShrinkParseError<'a> {
173    ParseFloat(ParseFloatError, &'a str),
174    NegativeValue(&'a str),
175}
176
177#[cfg(feature = "parser")]
178impl_debug_as_display!(FlexShrinkParseError<'a>);
179#[cfg(feature = "parser")]
180impl_display! { FlexShrinkParseError<'a>, {
181    ParseFloat(e, s) => format!("Invalid flex-shrink value: \"{}\". Reason: {}", s, e),
182    NegativeValue(s) => format!("Invalid flex-shrink value: \"{}\". Flex-shrink cannot be negative", s),
183}}
184
185#[cfg(feature = "parser")]
186#[derive(Debug, Clone, PartialEq)]
187pub enum FlexShrinkParseErrorOwned {
188    ParseFloat(ParseFloatError, String),
189    NegativeValue(String),
190}
191
192#[cfg(feature = "parser")]
193impl<'a> FlexShrinkParseError<'a> {
194    pub fn to_contained(&self) -> FlexShrinkParseErrorOwned {
195        match self {
196            FlexShrinkParseError::ParseFloat(e, s) => {
197                FlexShrinkParseErrorOwned::ParseFloat(e.clone(), s.to_string())
198            }
199            FlexShrinkParseError::NegativeValue(s) => {
200                FlexShrinkParseErrorOwned::NegativeValue(s.to_string())
201            }
202        }
203    }
204}
205
206#[cfg(feature = "parser")]
207impl FlexShrinkParseErrorOwned {
208    pub fn to_shared<'a>(&'a self) -> FlexShrinkParseError<'a> {
209        match self {
210            FlexShrinkParseErrorOwned::ParseFloat(e, s) => {
211                FlexShrinkParseError::ParseFloat(e.clone(), s.as_str())
212            }
213            FlexShrinkParseErrorOwned::NegativeValue(s) => {
214                FlexShrinkParseError::NegativeValue(s.as_str())
215            }
216        }
217    }
218}
219
220#[cfg(feature = "parser")]
221pub fn parse_layout_flex_shrink<'a>(
222    input: &'a str,
223) -> Result<LayoutFlexShrink, FlexShrinkParseError<'a>> {
224    match parse_float_value(input) {
225        Ok(o) => {
226            if o.get() < 0.0 {
227                Err(FlexShrinkParseError::NegativeValue(input))
228            } else {
229                Ok(LayoutFlexShrink { inner: o })
230            }
231        }
232        Err(e) => Err(FlexShrinkParseError::ParseFloat(e, input)),
233    }
234}
235
236// --- flex-direction ---
237
238/// Represents a `flex-direction` attribute, which establishes the main-axis,
239/// thus defining the direction flex items are placed in the flex container.
240/// Default: `Row`
241#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
242#[repr(C)]
243pub enum LayoutFlexDirection {
244    Row,
245    RowReverse,
246    Column,
247    ColumnReverse,
248}
249
250impl Default for LayoutFlexDirection {
251    fn default() -> Self {
252        Self::Row
253    }
254}
255
256#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
257#[repr(C)]
258pub enum LayoutAxis {
259    Horizontal,
260    Vertical,
261}
262
263impl LayoutFlexDirection {
264    pub fn get_axis(&self) -> LayoutAxis {
265        match self {
266            Self::Row | Self::RowReverse => LayoutAxis::Horizontal,
267            Self::Column | Self::ColumnReverse => LayoutAxis::Vertical,
268        }
269    }
270
271    pub fn is_reverse(&self) -> bool {
272        matches!(self, Self::RowReverse | Self::ColumnReverse)
273    }
274}
275
276impl PrintAsCssValue for LayoutFlexDirection {
277    fn print_as_css_value(&self) -> String {
278        String::from(match self {
279            Self::Row => "row",
280            Self::RowReverse => "row-reverse",
281            Self::Column => "column",
282            Self::ColumnReverse => "column-reverse",
283        })
284    }
285}
286// Formatting to Rust code
287impl crate::format_rust_code::FormatAsRustCode for LayoutFlexBasis {
288    fn format_as_rust_code(&self, _tabs: usize) -> String {
289        match self {
290            LayoutFlexBasis::Auto => String::from("LayoutFlexBasis::Auto"),
291            LayoutFlexBasis::Exact(px) => {
292                format!(
293                    "LayoutFlexBasis::Exact({})",
294                    crate::format_rust_code::format_pixel_value(px)
295                )
296            }
297        }
298    }
299}
300#[cfg(feature = "parser")]
301#[derive(Clone, PartialEq)]
302pub enum FlexDirectionParseError<'a> {
303    InvalidValue(&'a str),
304}
305
306#[cfg(feature = "parser")]
307impl_debug_as_display!(FlexDirectionParseError<'a>);
308#[cfg(feature = "parser")]
309impl_display! { FlexDirectionParseError<'a>, {
310    InvalidValue(s) => format!("Invalid flex-direction value: \"{}\"", s),
311}}
312
313#[cfg(feature = "parser")]
314#[derive(Debug, Clone, PartialEq)]
315pub enum FlexDirectionParseErrorOwned {
316    InvalidValue(String),
317}
318
319#[cfg(feature = "parser")]
320impl<'a> FlexDirectionParseError<'a> {
321    pub fn to_contained(&self) -> FlexDirectionParseErrorOwned {
322        match self {
323            Self::InvalidValue(s) => FlexDirectionParseErrorOwned::InvalidValue(s.to_string()),
324        }
325    }
326}
327
328#[cfg(feature = "parser")]
329impl FlexDirectionParseErrorOwned {
330    pub fn to_shared<'a>(&'a self) -> FlexDirectionParseError<'a> {
331        match self {
332            Self::InvalidValue(s) => FlexDirectionParseError::InvalidValue(s.as_str()),
333        }
334    }
335}
336
337#[cfg(feature = "parser")]
338pub fn parse_layout_flex_direction<'a>(
339    input: &'a str,
340) -> Result<LayoutFlexDirection, FlexDirectionParseError<'a>> {
341    match input.trim() {
342        "row" => Ok(LayoutFlexDirection::Row),
343        "row-reverse" => Ok(LayoutFlexDirection::RowReverse),
344        "column" => Ok(LayoutFlexDirection::Column),
345        "column-reverse" => Ok(LayoutFlexDirection::ColumnReverse),
346        _ => Err(FlexDirectionParseError::InvalidValue(input)),
347    }
348}
349
350// --- flex-wrap ---
351
352/// Represents a `flex-wrap` attribute, which determines whether flex items
353/// are forced onto one line or can wrap onto multiple lines.
354/// Default: `NoWrap`
355#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
356#[repr(C)]
357pub enum LayoutFlexWrap {
358    Wrap,
359    NoWrap,
360    WrapReverse,
361}
362
363impl Default for LayoutFlexWrap {
364    fn default() -> Self {
365        Self::NoWrap
366    }
367}
368
369impl PrintAsCssValue for LayoutFlexWrap {
370    fn print_as_css_value(&self) -> String {
371        String::from(match self {
372            Self::Wrap => "wrap",
373            Self::NoWrap => "nowrap",
374            Self::WrapReverse => "wrap-reverse",
375        })
376    }
377}
378
379#[cfg(feature = "parser")]
380#[derive(Clone, PartialEq)]
381pub enum FlexWrapParseError<'a> {
382    InvalidValue(&'a str),
383}
384
385#[cfg(feature = "parser")]
386impl_debug_as_display!(FlexWrapParseError<'a>);
387#[cfg(feature = "parser")]
388impl_display! { FlexWrapParseError<'a>, {
389    InvalidValue(s) => format!("Invalid flex-wrap value: \"{}\"", s),
390}}
391
392#[cfg(feature = "parser")]
393#[derive(Debug, Clone, PartialEq)]
394pub enum FlexWrapParseErrorOwned {
395    InvalidValue(String),
396}
397
398#[cfg(feature = "parser")]
399impl<'a> FlexWrapParseError<'a> {
400    pub fn to_contained(&self) -> FlexWrapParseErrorOwned {
401        match self {
402            Self::InvalidValue(s) => FlexWrapParseErrorOwned::InvalidValue(s.to_string()),
403        }
404    }
405}
406
407#[cfg(feature = "parser")]
408impl FlexWrapParseErrorOwned {
409    pub fn to_shared<'a>(&'a self) -> FlexWrapParseError<'a> {
410        match self {
411            Self::InvalidValue(s) => FlexWrapParseError::InvalidValue(s.as_str()),
412        }
413    }
414}
415
416#[cfg(feature = "parser")]
417pub fn parse_layout_flex_wrap<'a>(
418    input: &'a str,
419) -> Result<LayoutFlexWrap, FlexWrapParseError<'a>> {
420    match input.trim() {
421        "wrap" => Ok(LayoutFlexWrap::Wrap),
422        "nowrap" => Ok(LayoutFlexWrap::NoWrap),
423        "wrap-reverse" => Ok(LayoutFlexWrap::WrapReverse),
424        _ => Err(FlexWrapParseError::InvalidValue(input)),
425    }
426}
427
428// --- justify-content ---
429
430/// Represents a `justify-content` attribute, which defines the alignment
431/// along the main axis.
432/// Default: `Start` (flex-start)
433#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
434#[repr(C)]
435pub enum LayoutJustifyContent {
436    FlexStart,
437    FlexEnd,
438    Start,
439    End,
440    Center,
441    SpaceBetween,
442    SpaceAround,
443    SpaceEvenly,
444}
445
446impl Default for LayoutJustifyContent {
447    fn default() -> Self {
448        Self::Start
449    }
450}
451
452impl PrintAsCssValue for LayoutJustifyContent {
453    fn print_as_css_value(&self) -> String {
454        String::from(match self {
455            Self::Start => "start",
456            Self::End => "end",
457            Self::FlexStart => "flex-start",
458            Self::FlexEnd => "flex-end",
459            Self::Center => "center",
460            Self::SpaceBetween => "space-between",
461            Self::SpaceAround => "space-around",
462            Self::SpaceEvenly => "space-evenly",
463        })
464    }
465}
466
467#[cfg(feature = "parser")]
468#[derive(Clone, PartialEq)]
469pub enum JustifyContentParseError<'a> {
470    InvalidValue(&'a str),
471}
472
473#[cfg(feature = "parser")]
474impl_debug_as_display!(JustifyContentParseError<'a>);
475#[cfg(feature = "parser")]
476impl_display! { JustifyContentParseError<'a>, {
477    InvalidValue(s) => format!("Invalid justify-content value: \"{}\"", s),
478}}
479
480#[cfg(feature = "parser")]
481#[derive(Debug, Clone, PartialEq)]
482pub enum JustifyContentParseErrorOwned {
483    InvalidValue(String),
484}
485
486#[cfg(feature = "parser")]
487impl<'a> JustifyContentParseError<'a> {
488    pub fn to_contained(&self) -> JustifyContentParseErrorOwned {
489        match self {
490            Self::InvalidValue(s) => JustifyContentParseErrorOwned::InvalidValue(s.to_string()),
491        }
492    }
493}
494
495#[cfg(feature = "parser")]
496impl JustifyContentParseErrorOwned {
497    pub fn to_shared<'a>(&'a self) -> JustifyContentParseError<'a> {
498        match self {
499            Self::InvalidValue(s) => JustifyContentParseError::InvalidValue(s.as_str()),
500        }
501    }
502}
503
504#[cfg(feature = "parser")]
505pub fn parse_layout_justify_content<'a>(
506    input: &'a str,
507) -> Result<LayoutJustifyContent, JustifyContentParseError<'a>> {
508    match input.trim() {
509        "flex-start" => Ok(LayoutJustifyContent::Start),
510        "flex-end" => Ok(LayoutJustifyContent::End),
511        "center" => Ok(LayoutJustifyContent::Center),
512        "space-between" => Ok(LayoutJustifyContent::SpaceBetween),
513        "space-around" => Ok(LayoutJustifyContent::SpaceAround),
514        "space-evenly" => Ok(LayoutJustifyContent::SpaceEvenly),
515        _ => Err(JustifyContentParseError::InvalidValue(input)),
516    }
517}
518
519// --- align-items ---
520
521/// Represents an `align-items` attribute, which defines the default behavior for
522/// how flex items are laid out along the cross axis on the current line.
523/// Default: `Stretch`
524#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
525#[repr(C)]
526pub enum LayoutAlignItems {
527    Stretch,
528    Center,
529    Start,
530    End,
531    Baseline,
532}
533
534impl Default for LayoutAlignItems {
535    fn default() -> Self {
536        Self::Stretch
537    }
538}
539
540impl PrintAsCssValue for LayoutAlignItems {
541    fn print_as_css_value(&self) -> String {
542        String::from(match self {
543            Self::Stretch => "stretch",
544            Self::Center => "center",
545            Self::Start => "flex-start",
546            Self::End => "flex-end",
547            Self::Baseline => "baseline",
548        })
549    }
550}
551
552#[cfg(feature = "parser")]
553#[derive(Clone, PartialEq)]
554pub enum AlignItemsParseError<'a> {
555    InvalidValue(&'a str),
556}
557
558#[cfg(feature = "parser")]
559impl_debug_as_display!(AlignItemsParseError<'a>);
560#[cfg(feature = "parser")]
561impl_display! { AlignItemsParseError<'a>, {
562    InvalidValue(s) => format!("Invalid align-items value: \"{}\"", s),
563}}
564
565#[cfg(feature = "parser")]
566#[derive(Debug, Clone, PartialEq)]
567pub enum AlignItemsParseErrorOwned {
568    InvalidValue(String),
569}
570
571#[cfg(feature = "parser")]
572impl<'a> AlignItemsParseError<'a> {
573    pub fn to_contained(&self) -> AlignItemsParseErrorOwned {
574        match self {
575            Self::InvalidValue(s) => AlignItemsParseErrorOwned::InvalidValue(s.to_string()),
576        }
577    }
578}
579
580#[cfg(feature = "parser")]
581impl AlignItemsParseErrorOwned {
582    pub fn to_shared<'a>(&'a self) -> AlignItemsParseError<'a> {
583        match self {
584            Self::InvalidValue(s) => AlignItemsParseError::InvalidValue(s.as_str()),
585        }
586    }
587}
588
589#[cfg(feature = "parser")]
590pub fn parse_layout_align_items<'a>(
591    input: &'a str,
592) -> Result<LayoutAlignItems, AlignItemsParseError<'a>> {
593    match input.trim() {
594        "stretch" => Ok(LayoutAlignItems::Stretch),
595        "center" => Ok(LayoutAlignItems::Center),
596        "flex-start" => Ok(LayoutAlignItems::Start),
597        "flex-end" => Ok(LayoutAlignItems::End),
598        "baseline" => Ok(LayoutAlignItems::Baseline),
599        _ => Err(AlignItemsParseError::InvalidValue(input)),
600    }
601}
602
603// --- align-content ---
604
605/// Represents an `align-content` attribute, which aligns a flex container's lines
606/// within it when there is extra space in the cross-axis.
607/// Default: `Stretch`
608#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
609#[repr(C)]
610pub enum LayoutAlignContent {
611    Stretch,
612    Center,
613    Start,
614    End,
615    SpaceBetween,
616    SpaceAround,
617}
618
619impl Default for LayoutAlignContent {
620    fn default() -> Self {
621        Self::Stretch
622    }
623}
624
625impl PrintAsCssValue for LayoutAlignContent {
626    fn print_as_css_value(&self) -> String {
627        String::from(match self {
628            Self::Stretch => "stretch",
629            Self::Center => "center",
630            Self::Start => "flex-start",
631            Self::End => "flex-end",
632            Self::SpaceBetween => "space-between",
633            Self::SpaceAround => "space-around",
634        })
635    }
636}
637
638#[cfg(feature = "parser")]
639#[derive(Clone, PartialEq)]
640pub enum AlignContentParseError<'a> {
641    InvalidValue(&'a str),
642}
643
644#[cfg(feature = "parser")]
645impl_debug_as_display!(AlignContentParseError<'a>);
646#[cfg(feature = "parser")]
647impl_display! { AlignContentParseError<'a>, {
648    InvalidValue(s) => format!("Invalid align-content value: \"{}\"", s),
649}}
650
651#[cfg(feature = "parser")]
652#[derive(Debug, Clone, PartialEq)]
653pub enum AlignContentParseErrorOwned {
654    InvalidValue(String),
655}
656
657#[cfg(feature = "parser")]
658impl<'a> AlignContentParseError<'a> {
659    pub fn to_contained(&self) -> AlignContentParseErrorOwned {
660        match self {
661            Self::InvalidValue(s) => AlignContentParseErrorOwned::InvalidValue(s.to_string()),
662        }
663    }
664}
665
666#[cfg(feature = "parser")]
667impl AlignContentParseErrorOwned {
668    pub fn to_shared<'a>(&'a self) -> AlignContentParseError<'a> {
669        match self {
670            Self::InvalidValue(s) => AlignContentParseError::InvalidValue(s.as_str()),
671        }
672    }
673}
674
675#[cfg(feature = "parser")]
676pub fn parse_layout_align_content<'a>(
677    input: &'a str,
678) -> Result<LayoutAlignContent, AlignContentParseError<'a>> {
679    match input.trim() {
680        "stretch" => Ok(LayoutAlignContent::Stretch),
681        "center" => Ok(LayoutAlignContent::Center),
682        "flex-start" => Ok(LayoutAlignContent::Start),
683        "flex-end" => Ok(LayoutAlignContent::End),
684        "space-between" => Ok(LayoutAlignContent::SpaceBetween),
685        "space-around" => Ok(LayoutAlignContent::SpaceAround),
686        _ => Err(AlignContentParseError::InvalidValue(input)),
687    }
688}
689
690// --- align-self ---
691
692/// Represents an `align-self` attribute, which allows the default alignment
693/// (or the one specified by align-items) to be overridden for individual flex items.
694/// Default: `Auto`
695#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
696#[repr(C)]
697pub enum LayoutAlignSelf {
698    Auto,
699    Stretch,
700    Center,
701    Start,
702    End,
703    Baseline,
704}
705
706impl Default for LayoutAlignSelf {
707    fn default() -> Self {
708        Self::Auto
709    }
710}
711
712impl PrintAsCssValue for LayoutAlignSelf {
713    fn print_as_css_value(&self) -> String {
714        String::from(match self {
715            Self::Auto => "auto",
716            Self::Stretch => "stretch",
717            Self::Center => "center",
718            Self::Start => "flex-start",
719            Self::End => "flex-end",
720            Self::Baseline => "baseline",
721        })
722    }
723}
724
725impl FormatAsRustCode for LayoutAlignSelf {
726    fn format_as_rust_code(&self, _tabs: usize) -> String {
727        format!(
728            "LayoutAlignSelf::{}",
729            match self {
730                LayoutAlignSelf::Auto => "Auto",
731                LayoutAlignSelf::Stretch => "Stretch",
732                LayoutAlignSelf::Center => "Center",
733                LayoutAlignSelf::Start => "Start",
734                LayoutAlignSelf::End => "End",
735                LayoutAlignSelf::Baseline => "Baseline",
736            }
737        )
738    }
739}
740
741#[cfg(feature = "parser")]
742#[derive(Clone, PartialEq)]
743pub enum AlignSelfParseError<'a> {
744    InvalidValue(&'a str),
745}
746
747#[cfg(feature = "parser")]
748impl_debug_as_display!(AlignSelfParseError<'a>);
749#[cfg(feature = "parser")]
750impl_display! { AlignSelfParseError<'a>, {
751    InvalidValue(s) => format!("Invalid align-self value: \"{}\"", s),
752}}
753
754#[cfg(feature = "parser")]
755#[derive(Debug, Clone, PartialEq)]
756pub enum AlignSelfParseErrorOwned {
757    InvalidValue(String),
758}
759
760#[cfg(feature = "parser")]
761impl<'a> AlignSelfParseError<'a> {
762    pub fn to_contained(&self) -> AlignSelfParseErrorOwned {
763        match self {
764            Self::InvalidValue(s) => AlignSelfParseErrorOwned::InvalidValue(s.to_string()),
765        }
766    }
767}
768
769#[cfg(feature = "parser")]
770impl AlignSelfParseErrorOwned {
771    pub fn to_shared<'a>(&'a self) -> AlignSelfParseError<'a> {
772        match self {
773            Self::InvalidValue(s) => AlignSelfParseError::InvalidValue(s.as_str()),
774        }
775    }
776}
777
778#[cfg(feature = "parser")]
779pub fn parse_layout_align_self<'a>(
780    input: &'a str,
781) -> Result<LayoutAlignSelf, AlignSelfParseError<'a>> {
782    match input.trim() {
783        "auto" => Ok(LayoutAlignSelf::Auto),
784        "stretch" => Ok(LayoutAlignSelf::Stretch),
785        "center" => Ok(LayoutAlignSelf::Center),
786        "flex-start" => Ok(LayoutAlignSelf::Start),
787        "flex-end" => Ok(LayoutAlignSelf::End),
788        "baseline" => Ok(LayoutAlignSelf::Baseline),
789        _ => Err(AlignSelfParseError::InvalidValue(input)),
790    }
791}
792
793// --- flex-basis ---
794
795/// Represents a `flex-basis` attribute
796#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
797#[repr(C, u8)]
798pub enum LayoutFlexBasis {
799    /// auto
800    Auto,
801    /// Fixed size
802    Exact(crate::props::basic::pixel::PixelValue),
803}
804
805impl core::fmt::Debug for LayoutFlexBasis {
806    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
807        write!(f, "{}", self.print_as_css_value())
808    }
809}
810
811impl Default for LayoutFlexBasis {
812    fn default() -> Self {
813        LayoutFlexBasis::Auto
814    }
815}
816
817impl PrintAsCssValue for LayoutFlexBasis {
818    fn print_as_css_value(&self) -> String {
819        match self {
820            LayoutFlexBasis::Auto => "auto".to_string(),
821            LayoutFlexBasis::Exact(px) => px.print_as_css_value(),
822        }
823    }
824}
825
826#[cfg(feature = "parser")]
827#[derive(Clone, PartialEq)]
828pub enum FlexBasisParseError<'a> {
829    InvalidValue(&'a str),
830}
831
832#[cfg(feature = "parser")]
833impl_debug_as_display!(FlexBasisParseError<'a>);
834#[cfg(feature = "parser")]
835impl_display! { FlexBasisParseError<'a>, {
836    InvalidValue(e) => format!("Invalid flex-basis value: \"{}\"", e),
837}}
838
839#[cfg(feature = "parser")]
840#[derive(Debug, Clone, PartialEq)]
841pub enum FlexBasisParseErrorOwned {
842    InvalidValue(String),
843}
844
845#[cfg(feature = "parser")]
846impl<'a> FlexBasisParseError<'a> {
847    pub fn to_contained(&self) -> FlexBasisParseErrorOwned {
848        match self {
849            FlexBasisParseError::InvalidValue(s) => {
850                FlexBasisParseErrorOwned::InvalidValue(s.to_string())
851            }
852        }
853    }
854}
855
856#[cfg(feature = "parser")]
857impl FlexBasisParseErrorOwned {
858    pub fn to_shared<'a>(&'a self) -> FlexBasisParseError<'a> {
859        match self {
860            FlexBasisParseErrorOwned::InvalidValue(s) => {
861                FlexBasisParseError::InvalidValue(s.as_str())
862            }
863        }
864    }
865}
866
867#[cfg(feature = "parser")]
868pub fn parse_layout_flex_basis<'a>(
869    input: &'a str,
870) -> Result<LayoutFlexBasis, FlexBasisParseError<'a>> {
871    use crate::props::basic::pixel::parse_pixel_value;
872
873    match input.trim() {
874        "auto" => Ok(LayoutFlexBasis::Auto),
875        s => parse_pixel_value(s)
876            .map(LayoutFlexBasis::Exact)
877            .map_err(|_| FlexBasisParseError::InvalidValue(input)),
878    }
879}
880
881#[cfg(all(test, feature = "parser"))]
882mod tests {
883    use super::*;
884    use crate::props::basic::pixel::PixelValue;
885
886    #[test]
887    fn test_parse_layout_flex_grow() {
888        assert_eq!(parse_layout_flex_grow("0").unwrap().inner.get(), 0.0);
889        assert_eq!(parse_layout_flex_grow("1").unwrap().inner.get(), 1.0);
890        assert_eq!(parse_layout_flex_grow("2.5").unwrap().inner.get(), 2.5);
891        assert_eq!(parse_layout_flex_grow("  0.5  ").unwrap().inner.get(), 0.5);
892        assert!(parse_layout_flex_grow("none").is_err());
893        assert!(parse_layout_flex_grow("-1").is_err()); // Negative values are invalid
894    }
895
896    #[test]
897    fn test_parse_layout_flex_shrink() {
898        assert_eq!(parse_layout_flex_shrink("0").unwrap().inner.get(), 0.0);
899        assert_eq!(parse_layout_flex_shrink("1").unwrap().inner.get(), 1.0);
900        assert_eq!(parse_layout_flex_shrink("3.0").unwrap().inner.get(), 3.0);
901        assert_eq!(parse_layout_flex_shrink(" 0.2 ").unwrap().inner.get(), 0.2);
902        assert!(parse_layout_flex_shrink("auto").is_err());
903        assert!(parse_layout_flex_shrink("-1").is_err()); // Negative values are invalid
904    }
905
906    #[test]
907    fn test_parse_layout_flex_direction() {
908        assert_eq!(
909            parse_layout_flex_direction("row").unwrap(),
910            LayoutFlexDirection::Row
911        );
912        assert_eq!(
913            parse_layout_flex_direction("row-reverse").unwrap(),
914            LayoutFlexDirection::RowReverse
915        );
916        assert_eq!(
917            parse_layout_flex_direction("column").unwrap(),
918            LayoutFlexDirection::Column
919        );
920        assert_eq!(
921            parse_layout_flex_direction("column-reverse").unwrap(),
922            LayoutFlexDirection::ColumnReverse
923        );
924        assert_eq!(
925            parse_layout_flex_direction("  row  ").unwrap(),
926            LayoutFlexDirection::Row
927        );
928        assert!(parse_layout_flex_direction("reversed-row").is_err());
929    }
930
931    #[test]
932    fn test_parse_layout_flex_wrap() {
933        assert_eq!(
934            parse_layout_flex_wrap("nowrap").unwrap(),
935            LayoutFlexWrap::NoWrap
936        );
937        assert_eq!(
938            parse_layout_flex_wrap("wrap").unwrap(),
939            LayoutFlexWrap::Wrap
940        );
941        assert_eq!(
942            parse_layout_flex_wrap("wrap-reverse").unwrap(),
943            LayoutFlexWrap::WrapReverse
944        );
945        assert_eq!(
946            parse_layout_flex_wrap("  wrap  ").unwrap(),
947            LayoutFlexWrap::Wrap
948        );
949        assert!(parse_layout_flex_wrap("wrap reverse").is_err());
950    }
951
952    #[test]
953    fn test_parse_layout_justify_content() {
954        assert_eq!(
955            parse_layout_justify_content("flex-start").unwrap(),
956            LayoutJustifyContent::Start
957        );
958        assert_eq!(
959            parse_layout_justify_content("flex-end").unwrap(),
960            LayoutJustifyContent::End
961        );
962        assert_eq!(
963            parse_layout_justify_content("center").unwrap(),
964            LayoutJustifyContent::Center
965        );
966        assert_eq!(
967            parse_layout_justify_content("space-between").unwrap(),
968            LayoutJustifyContent::SpaceBetween
969        );
970        assert_eq!(
971            parse_layout_justify_content("space-around").unwrap(),
972            LayoutJustifyContent::SpaceAround
973        );
974        assert_eq!(
975            parse_layout_justify_content("space-evenly").unwrap(),
976            LayoutJustifyContent::SpaceEvenly
977        );
978        assert_eq!(
979            parse_layout_justify_content("  center  ").unwrap(),
980            LayoutJustifyContent::Center
981        );
982        assert!(parse_layout_justify_content("start").is_err());
983    }
984
985    #[test]
986    fn test_parse_layout_align_items() {
987        assert_eq!(
988            parse_layout_align_items("stretch").unwrap(),
989            LayoutAlignItems::Stretch
990        );
991        assert_eq!(
992            parse_layout_align_items("flex-start").unwrap(),
993            LayoutAlignItems::Start
994        );
995        assert_eq!(
996            parse_layout_align_items("flex-end").unwrap(),
997            LayoutAlignItems::End
998        );
999        assert_eq!(
1000            parse_layout_align_items("center").unwrap(),
1001            LayoutAlignItems::Center
1002        );
1003        assert_eq!(
1004            parse_layout_align_items("baseline").unwrap(),
1005            LayoutAlignItems::Baseline
1006        );
1007        assert!(parse_layout_align_items("end").is_err());
1008    }
1009
1010    #[test]
1011    fn test_parse_layout_align_content() {
1012        assert_eq!(
1013            parse_layout_align_content("stretch").unwrap(),
1014            LayoutAlignContent::Stretch
1015        );
1016        assert_eq!(
1017            parse_layout_align_content("flex-start").unwrap(),
1018            LayoutAlignContent::Start
1019        );
1020        assert_eq!(
1021            parse_layout_align_content("flex-end").unwrap(),
1022            LayoutAlignContent::End
1023        );
1024        assert_eq!(
1025            parse_layout_align_content("center").unwrap(),
1026            LayoutAlignContent::Center
1027        );
1028        assert_eq!(
1029            parse_layout_align_content("space-between").unwrap(),
1030            LayoutAlignContent::SpaceBetween
1031        );
1032        assert_eq!(
1033            parse_layout_align_content("space-around").unwrap(),
1034            LayoutAlignContent::SpaceAround
1035        );
1036        assert!(parse_layout_align_content("space-evenly").is_err()); // Not valid for align-content
1037    }
1038
1039    #[test]
1040    fn test_parse_layout_flex_basis() {
1041        assert_eq!(
1042            parse_layout_flex_basis("auto").unwrap(),
1043            LayoutFlexBasis::Auto
1044        );
1045        assert_eq!(
1046            parse_layout_flex_basis("200px").unwrap(),
1047            LayoutFlexBasis::Exact(PixelValue::px(200.0))
1048        );
1049        assert_eq!(
1050            parse_layout_flex_basis("50%").unwrap(),
1051            LayoutFlexBasis::Exact(PixelValue::percent(50.0))
1052        );
1053        assert_eq!(
1054            parse_layout_flex_basis("  10em  ").unwrap(),
1055            LayoutFlexBasis::Exact(PixelValue::em(10.0))
1056        );
1057        assert!(parse_layout_flex_basis("none").is_err());
1058        // Liberal parsing accepts unitless numbers (treated as px)
1059        assert_eq!(
1060            parse_layout_flex_basis("200").unwrap(),
1061            LayoutFlexBasis::Exact(PixelValue::px(200.0))
1062        );
1063        assert_eq!(
1064            parse_layout_flex_basis("0").unwrap(),
1065            LayoutFlexBasis::Exact(PixelValue::px(0.0))
1066        );
1067    }
1068}