1
//! CSS properties for flexbox layout.
2

            
3
use alloc::string::{String, ToString};
4
use core::num::ParseFloatError;
5
use crate::corety::AzString;
6

            
7
use crate::{
8
    format_rust_code::FormatAsRustCode,
9
    props::{
10
        basic::{
11
            error::ParseFloatErrorWithInput,
12
            length::{parse_float_value, FloatValue},
13
        },
14
        formatter::PrintAsCssValue,
15
    },
16
};
17

            
18
// --- flex-grow ---
19

            
20
/// Represents a `flex-grow` attribute, which dictates what proportion of the
21
/// remaining space in the flex container should be assigned to the item.
22
/// Default: 0
23
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
24
#[repr(C)]
25
pub struct LayoutFlexGrow {
26
    pub inner: FloatValue,
27
}
28

            
29
impl core::fmt::Debug for LayoutFlexGrow {
30
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
31
        write!(f, "{}", self.inner.get())
32
    }
33
}
34

            
35
impl Default for LayoutFlexGrow {
36
    fn default() -> Self {
37
        Self {
38
            inner: FloatValue::const_new(0),
39
        }
40
    }
41
}
42

            
43
impl PrintAsCssValue for LayoutFlexGrow {
44
    fn print_as_css_value(&self) -> String {
45
        format!("{}", self.inner)
46
    }
47
}
48

            
49
impl LayoutFlexGrow {
50
    pub fn new(value: isize) -> Self {
51
        Self {
52
            inner: FloatValue::new(value as f32),
53
        }
54
    }
55

            
56
    pub const fn const_new(value: isize) -> Self {
57
        Self {
58
            inner: FloatValue::const_new(value),
59
        }
60
    }
61

            
62
    pub fn interpolate(&self, other: &Self, t: f32) -> Self {
63
        Self {
64
            inner: self.inner.interpolate(&other.inner, t),
65
        }
66
    }
67
}
68

            
69
#[cfg(feature = "parser")]
70
#[derive(Clone, PartialEq)]
71
pub enum FlexGrowParseError<'a> {
72
    ParseFloat(ParseFloatError, &'a str),
73
    NegativeValue(&'a str),
74
}
75

            
76
#[cfg(feature = "parser")]
77
impl_debug_as_display!(FlexGrowParseError<'a>);
78
#[cfg(feature = "parser")]
79
impl_display! { FlexGrowParseError<'a>, {
80
    ParseFloat(e, s) => format!("Invalid flex-grow value: \"{}\". Reason: {}", s, e),
81
    NegativeValue(s) => format!("Invalid flex-grow value: \"{}\". Flex-grow cannot be negative", s),
82
}}
83

            
84
#[cfg(feature = "parser")]
85
#[derive(Debug, Clone, PartialEq)]
86
#[repr(C, u8)]
87
pub enum FlexGrowParseErrorOwned {
88
    ParseFloat(ParseFloatErrorWithInput),
89
    NegativeValue(AzString),
90
}
91

            
92
#[cfg(feature = "parser")]
93
impl<'a> FlexGrowParseError<'a> {
94
    pub fn to_contained(&self) -> FlexGrowParseErrorOwned {
95
        match self {
96
            FlexGrowParseError::ParseFloat(e, s) => {
97
                FlexGrowParseErrorOwned::ParseFloat(ParseFloatErrorWithInput { error: e.clone().into(), input: s.to_string().into() })
98
            }
99
            FlexGrowParseError::NegativeValue(s) => {
100
                FlexGrowParseErrorOwned::NegativeValue(s.to_string().into())
101
            }
102
        }
103
    }
104
}
105

            
106
#[cfg(feature = "parser")]
107
impl FlexGrowParseErrorOwned {
108
    pub fn to_shared<'a>(&'a self) -> FlexGrowParseError<'a> {
109
        match self {
110
            FlexGrowParseErrorOwned::ParseFloat(e) => {
111
                FlexGrowParseError::ParseFloat(e.error.to_std(), e.input.as_str())
112
            }
113
            FlexGrowParseErrorOwned::NegativeValue(s) => {
114
                FlexGrowParseError::NegativeValue(s.as_str())
115
            }
116
        }
117
    }
118
}
119

            
120
#[cfg(feature = "parser")]
121
223
pub fn parse_layout_flex_grow<'a>(
122
223
    input: &'a str,
123
223
) -> Result<LayoutFlexGrow, FlexGrowParseError<'a>> {
124
223
    match parse_float_value(input) {
125
222
        Ok(o) => {
126
222
            if o.get() < 0.0 {
127
1
                Err(FlexGrowParseError::NegativeValue(input))
128
            } else {
129
221
                Ok(LayoutFlexGrow { inner: o })
130
            }
131
        }
132
1
        Err(e) => Err(FlexGrowParseError::ParseFloat(e, input)),
133
    }
134
223
}
135

            
136
// --- flex-shrink ---
137

            
138
/// Represents a `flex-shrink` attribute, which dictates what proportion of
139
/// the negative space in the flex container should be removed from the item.
140
/// Default: 1
141
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
142
#[repr(C)]
143
pub struct LayoutFlexShrink {
144
    pub inner: FloatValue,
145
}
146

            
147
impl core::fmt::Debug for LayoutFlexShrink {
148
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
149
        write!(f, "{}", self.inner.get())
150
    }
151
}
152

            
153
impl Default for LayoutFlexShrink {
154
    fn default() -> Self {
155
        Self {
156
            inner: FloatValue::const_new(1),
157
        }
158
    }
159
}
160

            
161
impl PrintAsCssValue for LayoutFlexShrink {
162
    fn print_as_css_value(&self) -> String {
163
        format!("{}", self.inner)
164
    }
165
}
166

            
167
impl LayoutFlexShrink {
168
    pub fn interpolate(&self, other: &Self, t: f32) -> Self {
169
        Self {
170
            inner: self.inner.interpolate(&other.inner, t),
171
        }
172
    }
173
}
174

            
175
#[cfg(feature = "parser")]
176
#[derive(Clone, PartialEq)]
177
pub enum FlexShrinkParseError<'a> {
178
    ParseFloat(ParseFloatError, &'a str),
179
    NegativeValue(&'a str),
180
}
181

            
182
#[cfg(feature = "parser")]
183
impl_debug_as_display!(FlexShrinkParseError<'a>);
184
#[cfg(feature = "parser")]
185
impl_display! { FlexShrinkParseError<'a>, {
186
    ParseFloat(e, s) => format!("Invalid flex-shrink value: \"{}\". Reason: {}", s, e),
187
    NegativeValue(s) => format!("Invalid flex-shrink value: \"{}\". Flex-shrink cannot be negative", s),
188
}}
189

            
190
#[cfg(feature = "parser")]
191
#[derive(Debug, Clone, PartialEq)]
192
#[repr(C, u8)]
193
pub enum FlexShrinkParseErrorOwned {
194
    ParseFloat(ParseFloatErrorWithInput),
195
    NegativeValue(AzString),
196
}
197

            
198
#[cfg(feature = "parser")]
199
impl<'a> FlexShrinkParseError<'a> {
200
    pub fn to_contained(&self) -> FlexShrinkParseErrorOwned {
201
        match self {
202
            FlexShrinkParseError::ParseFloat(e, s) => {
203
                FlexShrinkParseErrorOwned::ParseFloat(ParseFloatErrorWithInput { error: e.clone().into(), input: s.to_string().into() })
204
            }
205
            FlexShrinkParseError::NegativeValue(s) => {
206
                FlexShrinkParseErrorOwned::NegativeValue(s.to_string().into())
207
            }
208
        }
209
    }
210
}
211

            
212
#[cfg(feature = "parser")]
213
impl FlexShrinkParseErrorOwned {
214
    pub fn to_shared<'a>(&'a self) -> FlexShrinkParseError<'a> {
215
        match self {
216
            FlexShrinkParseErrorOwned::ParseFloat(e) => {
217
                FlexShrinkParseError::ParseFloat(e.error.to_std(), e.input.as_str())
218
            }
219
            FlexShrinkParseErrorOwned::NegativeValue(s) => {
220
                FlexShrinkParseError::NegativeValue(s.as_str())
221
            }
222
        }
223
    }
224
}
225

            
226
#[cfg(feature = "parser")]
227
34
pub fn parse_layout_flex_shrink<'a>(
228
34
    input: &'a str,
229
34
) -> Result<LayoutFlexShrink, FlexShrinkParseError<'a>> {
230
34
    match parse_float_value(input) {
231
26
        Ok(o) => {
232
26
            if o.get() < 0.0 {
233
1
                Err(FlexShrinkParseError::NegativeValue(input))
234
            } else {
235
25
                Ok(LayoutFlexShrink { inner: o })
236
            }
237
        }
238
8
        Err(e) => Err(FlexShrinkParseError::ParseFloat(e, input)),
239
    }
240
34
}
241

            
242
// --- flex-direction ---
243

            
244
/// Represents a `flex-direction` attribute, which establishes the main-axis,
245
/// thus defining the direction flex items are placed in the flex container.
246
/// Default: `Row`
247
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
248
#[repr(C)]
249
#[derive(Default)]
250
pub enum LayoutFlexDirection {
251
    #[default]
252
    Row,
253
    RowReverse,
254
    Column,
255
    ColumnReverse,
256
}
257

            
258

            
259
/// Represents the main or cross axis of a flex container.
260
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
261
#[repr(C)]
262
pub enum LayoutAxis {
263
    Horizontal,
264
    Vertical,
265
}
266

            
267
impl LayoutFlexDirection {
268
    pub fn get_axis(&self) -> LayoutAxis {
269
        match self {
270
            Self::Row | Self::RowReverse => LayoutAxis::Horizontal,
271
            Self::Column | Self::ColumnReverse => LayoutAxis::Vertical,
272
        }
273
    }
274

            
275
    pub fn is_reverse(&self) -> bool {
276
        matches!(self, Self::RowReverse | Self::ColumnReverse)
277
    }
278
}
279

            
280
impl PrintAsCssValue for LayoutFlexDirection {
281
    fn print_as_css_value(&self) -> String {
282
        String::from(match self {
283
            Self::Row => "row",
284
            Self::RowReverse => "row-reverse",
285
            Self::Column => "column",
286
            Self::ColumnReverse => "column-reverse",
287
        })
288
    }
289
}
290

            
291
#[cfg(feature = "parser")]
292
#[derive(Clone, PartialEq)]
293
pub enum FlexDirectionParseError<'a> {
294
    InvalidValue(&'a str),
295
}
296

            
297
#[cfg(feature = "parser")]
298
impl_debug_as_display!(FlexDirectionParseError<'a>);
299
#[cfg(feature = "parser")]
300
impl_display! { FlexDirectionParseError<'a>, {
301
    InvalidValue(s) => format!("Invalid flex-direction value: \"{}\"", s),
302
}}
303

            
304
#[cfg(feature = "parser")]
305
#[derive(Debug, Clone, PartialEq)]
306
#[repr(C, u8)]
307
pub enum FlexDirectionParseErrorOwned {
308
    InvalidValue(AzString),
309
}
310

            
311
#[cfg(feature = "parser")]
312
impl<'a> FlexDirectionParseError<'a> {
313
    pub fn to_contained(&self) -> FlexDirectionParseErrorOwned {
314
        match self {
315
            Self::InvalidValue(s) => FlexDirectionParseErrorOwned::InvalidValue(s.to_string().into()),
316
        }
317
    }
318
}
319

            
320
#[cfg(feature = "parser")]
321
impl FlexDirectionParseErrorOwned {
322
    pub fn to_shared<'a>(&'a self) -> FlexDirectionParseError<'a> {
323
        match self {
324
            Self::InvalidValue(s) => FlexDirectionParseError::InvalidValue(s.as_str()),
325
        }
326
    }
327
}
328

            
329
#[cfg(feature = "parser")]
330
333
pub fn parse_layout_flex_direction<'a>(
331
333
    input: &'a str,
332
333
) -> Result<LayoutFlexDirection, FlexDirectionParseError<'a>> {
333
333
    match input.trim() {
334
333
        "row" => Ok(LayoutFlexDirection::Row),
335
240
        "row-reverse" => Ok(LayoutFlexDirection::RowReverse),
336
239
        "column" => Ok(LayoutFlexDirection::Column),
337
2
        "column-reverse" => Ok(LayoutFlexDirection::ColumnReverse),
338
1
        _ => Err(FlexDirectionParseError::InvalidValue(input)),
339
    }
340
333
}
341

            
342
// --- flex-wrap ---
343

            
344
/// Represents a `flex-wrap` attribute, which determines whether flex items
345
/// are forced onto one line or can wrap onto multiple lines.
346
/// Default: `NoWrap`
347
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
348
#[repr(C)]
349
#[derive(Default)]
350
pub enum LayoutFlexWrap {
351
    Wrap,
352
    #[default]
353
    NoWrap,
354
    WrapReverse,
355
}
356

            
357

            
358
impl PrintAsCssValue for LayoutFlexWrap {
359
    fn print_as_css_value(&self) -> String {
360
        String::from(match self {
361
            Self::Wrap => "wrap",
362
            Self::NoWrap => "nowrap",
363
            Self::WrapReverse => "wrap-reverse",
364
        })
365
    }
366
}
367

            
368
#[cfg(feature = "parser")]
369
#[derive(Clone, PartialEq)]
370
pub enum FlexWrapParseError<'a> {
371
    InvalidValue(&'a str),
372
}
373

            
374
#[cfg(feature = "parser")]
375
impl_debug_as_display!(FlexWrapParseError<'a>);
376
#[cfg(feature = "parser")]
377
impl_display! { FlexWrapParseError<'a>, {
378
    InvalidValue(s) => format!("Invalid flex-wrap value: \"{}\"", s),
379
}}
380

            
381
#[cfg(feature = "parser")]
382
#[derive(Debug, Clone, PartialEq)]
383
#[repr(C, u8)]
384
pub enum FlexWrapParseErrorOwned {
385
    InvalidValue(AzString),
386
}
387

            
388
#[cfg(feature = "parser")]
389
impl<'a> FlexWrapParseError<'a> {
390
    pub fn to_contained(&self) -> FlexWrapParseErrorOwned {
391
        match self {
392
            Self::InvalidValue(s) => FlexWrapParseErrorOwned::InvalidValue(s.to_string().into()),
393
        }
394
    }
395
}
396

            
397
#[cfg(feature = "parser")]
398
impl FlexWrapParseErrorOwned {
399
    pub fn to_shared<'a>(&'a self) -> FlexWrapParseError<'a> {
400
        match self {
401
            Self::InvalidValue(s) => FlexWrapParseError::InvalidValue(s.as_str()),
402
        }
403
    }
404
}
405

            
406
#[cfg(feature = "parser")]
407
24
pub fn parse_layout_flex_wrap<'a>(
408
24
    input: &'a str,
409
24
) -> Result<LayoutFlexWrap, FlexWrapParseError<'a>> {
410
24
    match input.trim() {
411
24
        "wrap" => Ok(LayoutFlexWrap::Wrap),
412
3
        "nowrap" => Ok(LayoutFlexWrap::NoWrap),
413
2
        "wrap-reverse" => Ok(LayoutFlexWrap::WrapReverse),
414
1
        _ => Err(FlexWrapParseError::InvalidValue(input)),
415
    }
416
24
}
417

            
418
// --- justify-content ---
419

            
420
/// Represents a `justify-content` attribute, which defines the alignment
421
/// along the main axis.
422
/// Default: `Start` (flex-start)
423
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
424
#[repr(C)]
425
#[derive(Default)]
426
pub enum LayoutJustifyContent {
427
    FlexStart,
428
    FlexEnd,
429
    #[default]
430
    Start,
431
    End,
432
    Center,
433
    SpaceBetween,
434
    SpaceAround,
435
    SpaceEvenly,
436
}
437

            
438

            
439
impl PrintAsCssValue for LayoutJustifyContent {
440
    fn print_as_css_value(&self) -> String {
441
        String::from(match self {
442
            Self::Start => "start",
443
            Self::End => "end",
444
            Self::FlexStart => "flex-start",
445
            Self::FlexEnd => "flex-end",
446
            Self::Center => "center",
447
            Self::SpaceBetween => "space-between",
448
            Self::SpaceAround => "space-around",
449
            Self::SpaceEvenly => "space-evenly",
450
        })
451
    }
452
}
453

            
454
#[cfg(feature = "parser")]
455
#[derive(Clone, PartialEq)]
456
pub enum JustifyContentParseError<'a> {
457
    InvalidValue(&'a str),
458
}
459

            
460
#[cfg(feature = "parser")]
461
impl_debug_as_display!(JustifyContentParseError<'a>);
462
#[cfg(feature = "parser")]
463
impl_display! { JustifyContentParseError<'a>, {
464
    InvalidValue(s) => format!("Invalid justify-content value: \"{}\"", s),
465
}}
466

            
467
#[cfg(feature = "parser")]
468
#[derive(Debug, Clone, PartialEq)]
469
#[repr(C, u8)]
470
pub enum JustifyContentParseErrorOwned {
471
    InvalidValue(AzString),
472
}
473

            
474
#[cfg(feature = "parser")]
475
impl<'a> JustifyContentParseError<'a> {
476
    pub fn to_contained(&self) -> JustifyContentParseErrorOwned {
477
        match self {
478
            Self::InvalidValue(s) => JustifyContentParseErrorOwned::InvalidValue(s.to_string().into()),
479
        }
480
    }
481
}
482

            
483
#[cfg(feature = "parser")]
484
impl JustifyContentParseErrorOwned {
485
    pub fn to_shared<'a>(&'a self) -> JustifyContentParseError<'a> {
486
        match self {
487
            Self::InvalidValue(s) => JustifyContentParseError::InvalidValue(s.as_str()),
488
        }
489
    }
490
}
491

            
492
#[cfg(feature = "parser")]
493
70
pub fn parse_layout_justify_content<'a>(
494
70
    input: &'a str,
495
70
) -> Result<LayoutJustifyContent, JustifyContentParseError<'a>> {
496
70
    match input.trim() {
497
70
        "flex-start" => Ok(LayoutJustifyContent::FlexStart),
498
69
        "flex-end" => Ok(LayoutJustifyContent::FlexEnd),
499
68
        "start" => Ok(LayoutJustifyContent::Start),
500
67
        "end" => Ok(LayoutJustifyContent::End),
501
66
        "center" => Ok(LayoutJustifyContent::Center),
502
3
        "space-between" => Ok(LayoutJustifyContent::SpaceBetween),
503
2
        "space-around" => Ok(LayoutJustifyContent::SpaceAround),
504
1
        "space-evenly" => Ok(LayoutJustifyContent::SpaceEvenly),
505
        _ => Err(JustifyContentParseError::InvalidValue(input)),
506
    }
507
70
}
508

            
509
// --- align-items ---
510

            
511
/// Represents an `align-items` attribute, which defines the default behavior for
512
/// how flex items are laid out along the cross axis on the current line.
513
/// Default: `Stretch`
514
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
515
#[repr(C)]
516
#[derive(Default)]
517
pub enum LayoutAlignItems {
518
    #[default]
519
    Stretch,
520
    Center,
521
    Start,
522
    End,
523
    Baseline,
524
}
525

            
526

            
527
impl PrintAsCssValue for LayoutAlignItems {
528
    fn print_as_css_value(&self) -> String {
529
        String::from(match self {
530
            Self::Stretch => "stretch",
531
            Self::Center => "center",
532
            Self::Start => "flex-start",
533
            Self::End => "flex-end",
534
            Self::Baseline => "baseline",
535
        })
536
    }
537
}
538

            
539
#[cfg(feature = "parser")]
540
#[derive(Clone, PartialEq)]
541
pub enum AlignItemsParseError<'a> {
542
    InvalidValue(&'a str),
543
}
544

            
545
#[cfg(feature = "parser")]
546
impl_debug_as_display!(AlignItemsParseError<'a>);
547
#[cfg(feature = "parser")]
548
impl_display! { AlignItemsParseError<'a>, {
549
    InvalidValue(s) => format!("Invalid align-items value: \"{}\"", s),
550
}}
551

            
552
#[cfg(feature = "parser")]
553
#[derive(Debug, Clone, PartialEq)]
554
#[repr(C, u8)]
555
pub enum AlignItemsParseErrorOwned {
556
    InvalidValue(AzString),
557
}
558

            
559
#[cfg(feature = "parser")]
560
impl<'a> AlignItemsParseError<'a> {
561
    pub fn to_contained(&self) -> AlignItemsParseErrorOwned {
562
        match self {
563
            Self::InvalidValue(s) => AlignItemsParseErrorOwned::InvalidValue(s.to_string().into()),
564
        }
565
    }
566
}
567

            
568
#[cfg(feature = "parser")]
569
impl AlignItemsParseErrorOwned {
570
    pub fn to_shared<'a>(&'a self) -> AlignItemsParseError<'a> {
571
        match self {
572
            Self::InvalidValue(s) => AlignItemsParseError::InvalidValue(s.as_str()),
573
        }
574
    }
575
}
576

            
577
#[cfg(feature = "parser")]
578
69
pub fn parse_layout_align_items<'a>(
579
69
    input: &'a str,
580
69
) -> Result<LayoutAlignItems, AlignItemsParseError<'a>> {
581
69
    match input.trim() {
582
69
        "stretch" => Ok(LayoutAlignItems::Stretch),
583
68
        "center" => Ok(LayoutAlignItems::Center),
584
6
        "start" | "flex-start" => Ok(LayoutAlignItems::Start),
585
4
        "end" | "flex-end" => Ok(LayoutAlignItems::End),
586
2
        "baseline" => Ok(LayoutAlignItems::Baseline),
587
1
        _ => Err(AlignItemsParseError::InvalidValue(input)),
588
    }
589
69
}
590

            
591
// --- align-content ---
592

            
593
/// Represents an `align-content` attribute, which aligns a flex container's lines
594
/// within it when there is extra space in the cross-axis.
595
/// Default: `Stretch`
596
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
597
#[repr(C)]
598
#[derive(Default)]
599
pub enum LayoutAlignContent {
600
    #[default]
601
    Stretch,
602
    Center,
603
    Start,
604
    End,
605
    SpaceBetween,
606
    SpaceAround,
607
}
608

            
609

            
610
impl PrintAsCssValue for LayoutAlignContent {
611
    fn print_as_css_value(&self) -> String {
612
        String::from(match self {
613
            Self::Stretch => "stretch",
614
            Self::Center => "center",
615
            Self::Start => "flex-start",
616
            Self::End => "flex-end",
617
            Self::SpaceBetween => "space-between",
618
            Self::SpaceAround => "space-around",
619
        })
620
    }
621
}
622

            
623
#[cfg(feature = "parser")]
624
#[derive(Clone, PartialEq)]
625
pub enum AlignContentParseError<'a> {
626
    InvalidValue(&'a str),
627
}
628

            
629
#[cfg(feature = "parser")]
630
impl_debug_as_display!(AlignContentParseError<'a>);
631
#[cfg(feature = "parser")]
632
impl_display! { AlignContentParseError<'a>, {
633
    InvalidValue(s) => format!("Invalid align-content value: \"{}\"", s),
634
}}
635

            
636
#[cfg(feature = "parser")]
637
#[derive(Debug, Clone, PartialEq)]
638
#[repr(C, u8)]
639
pub enum AlignContentParseErrorOwned {
640
    InvalidValue(AzString),
641
}
642

            
643
#[cfg(feature = "parser")]
644
impl<'a> AlignContentParseError<'a> {
645
    pub fn to_contained(&self) -> AlignContentParseErrorOwned {
646
        match self {
647
            Self::InvalidValue(s) => AlignContentParseErrorOwned::InvalidValue(s.to_string().into()),
648
        }
649
    }
650
}
651

            
652
#[cfg(feature = "parser")]
653
impl AlignContentParseErrorOwned {
654
    pub fn to_shared<'a>(&'a self) -> AlignContentParseError<'a> {
655
        match self {
656
            Self::InvalidValue(s) => AlignContentParseError::InvalidValue(s.as_str()),
657
        }
658
    }
659
}
660

            
661
#[cfg(feature = "parser")]
662
7
pub fn parse_layout_align_content<'a>(
663
7
    input: &'a str,
664
7
) -> Result<LayoutAlignContent, AlignContentParseError<'a>> {
665
7
    match input.trim() {
666
7
        "stretch" => Ok(LayoutAlignContent::Stretch),
667
6
        "center" => Ok(LayoutAlignContent::Center),
668
5
        "start" | "flex-start" => Ok(LayoutAlignContent::Start),
669
4
        "end" | "flex-end" => Ok(LayoutAlignContent::End),
670
3
        "space-between" => Ok(LayoutAlignContent::SpaceBetween),
671
2
        "space-around" => Ok(LayoutAlignContent::SpaceAround),
672
1
        _ => Err(AlignContentParseError::InvalidValue(input)),
673
    }
674
7
}
675

            
676
// --- align-self ---
677

            
678
/// Represents an `align-self` attribute, which allows the default alignment
679
/// (or the one specified by align-items) to be overridden for individual flex items.
680
/// Default: `Auto`
681
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
682
#[repr(C)]
683
#[derive(Default)]
684
pub enum LayoutAlignSelf {
685
    #[default]
686
    Auto,
687
    Stretch,
688
    Center,
689
    Start,
690
    End,
691
    Baseline,
692
}
693

            
694

            
695
impl PrintAsCssValue for LayoutAlignSelf {
696
    fn print_as_css_value(&self) -> String {
697
        String::from(match self {
698
            Self::Auto => "auto",
699
            Self::Stretch => "stretch",
700
            Self::Center => "center",
701
            Self::Start => "flex-start",
702
            Self::End => "flex-end",
703
            Self::Baseline => "baseline",
704
        })
705
    }
706
}
707

            
708
impl FormatAsRustCode for LayoutAlignSelf {
709
    fn format_as_rust_code(&self, _tabs: usize) -> String {
710
        format!(
711
            "LayoutAlignSelf::{}",
712
            match self {
713
                LayoutAlignSelf::Auto => "Auto",
714
                LayoutAlignSelf::Stretch => "Stretch",
715
                LayoutAlignSelf::Center => "Center",
716
                LayoutAlignSelf::Start => "Start",
717
                LayoutAlignSelf::End => "End",
718
                LayoutAlignSelf::Baseline => "Baseline",
719
            }
720
        )
721
    }
722
}
723

            
724
#[cfg(feature = "parser")]
725
#[derive(Clone, PartialEq)]
726
pub enum AlignSelfParseError<'a> {
727
    InvalidValue(&'a str),
728
}
729

            
730
#[cfg(feature = "parser")]
731
impl_debug_as_display!(AlignSelfParseError<'a>);
732
#[cfg(feature = "parser")]
733
impl_display! { AlignSelfParseError<'a>, {
734
    InvalidValue(s) => format!("Invalid align-self value: \"{}\"", s),
735
}}
736

            
737
#[cfg(feature = "parser")]
738
#[derive(Debug, Clone, PartialEq)]
739
#[repr(C, u8)]
740
pub enum AlignSelfParseErrorOwned {
741
    InvalidValue(AzString),
742
}
743

            
744
#[cfg(feature = "parser")]
745
impl<'a> AlignSelfParseError<'a> {
746
    pub fn to_contained(&self) -> AlignSelfParseErrorOwned {
747
        match self {
748
            Self::InvalidValue(s) => AlignSelfParseErrorOwned::InvalidValue(s.to_string().into()),
749
        }
750
    }
751
}
752

            
753
#[cfg(feature = "parser")]
754
impl AlignSelfParseErrorOwned {
755
    pub fn to_shared<'a>(&'a self) -> AlignSelfParseError<'a> {
756
        match self {
757
            Self::InvalidValue(s) => AlignSelfParseError::InvalidValue(s.as_str()),
758
        }
759
    }
760
}
761

            
762
#[cfg(feature = "parser")]
763
42
pub fn parse_layout_align_self<'a>(
764
42
    input: &'a str,
765
42
) -> Result<LayoutAlignSelf, AlignSelfParseError<'a>> {
766
42
    match input.trim() {
767
42
        "auto" => Ok(LayoutAlignSelf::Auto),
768
42
        "stretch" => Ok(LayoutAlignSelf::Stretch),
769
        "center" => Ok(LayoutAlignSelf::Center),
770
        "start" | "flex-start" => Ok(LayoutAlignSelf::Start),
771
        "end" | "flex-end" => Ok(LayoutAlignSelf::End),
772
        "baseline" => Ok(LayoutAlignSelf::Baseline),
773
        _ => Err(AlignSelfParseError::InvalidValue(input)),
774
    }
775
42
}
776

            
777
// --- flex-basis ---
778

            
779
/// Represents a `flex-basis` attribute
780
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
781
#[repr(C, u8)]
782
#[derive(Default)]
783
pub enum LayoutFlexBasis {
784
    /// auto
785
    #[default]
786
    Auto,
787
    /// Fixed size
788
    Exact(crate::props::basic::pixel::PixelValue),
789
}
790

            
791
impl core::fmt::Debug for LayoutFlexBasis {
792
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
793
        write!(f, "{}", self.print_as_css_value())
794
    }
795
}
796

            
797

            
798
impl PrintAsCssValue for LayoutFlexBasis {
799
    fn print_as_css_value(&self) -> String {
800
        match self {
801
            LayoutFlexBasis::Auto => "auto".to_string(),
802
            LayoutFlexBasis::Exact(px) => px.print_as_css_value(),
803
        }
804
    }
805
}
806

            
807
impl crate::format_rust_code::FormatAsRustCode for LayoutFlexBasis {
808
    fn format_as_rust_code(&self, _tabs: usize) -> String {
809
        match self {
810
            LayoutFlexBasis::Auto => String::from("LayoutFlexBasis::Auto"),
811
            LayoutFlexBasis::Exact(px) => {
812
                format!(
813
                    "LayoutFlexBasis::Exact({})",
814
                    crate::format_rust_code::format_pixel_value(px)
815
                )
816
            }
817
        }
818
    }
819
}
820

            
821
#[cfg(feature = "parser")]
822
#[derive(Clone, PartialEq)]
823
pub enum FlexBasisParseError<'a> {
824
    InvalidValue(&'a str),
825
}
826

            
827
#[cfg(feature = "parser")]
828
impl_debug_as_display!(FlexBasisParseError<'a>);
829
#[cfg(feature = "parser")]
830
impl_display! { FlexBasisParseError<'a>, {
831
    InvalidValue(e) => format!("Invalid flex-basis value: \"{}\"", e),
832
}}
833

            
834
#[cfg(feature = "parser")]
835
#[derive(Debug, Clone, PartialEq)]
836
#[repr(C, u8)]
837
pub enum FlexBasisParseErrorOwned {
838
    InvalidValue(AzString),
839
}
840

            
841
#[cfg(feature = "parser")]
842
impl<'a> FlexBasisParseError<'a> {
843
    pub fn to_contained(&self) -> FlexBasisParseErrorOwned {
844
        match self {
845
            FlexBasisParseError::InvalidValue(s) => {
846
                FlexBasisParseErrorOwned::InvalidValue(s.to_string().into())
847
            }
848
        }
849
    }
850
}
851

            
852
#[cfg(feature = "parser")]
853
impl FlexBasisParseErrorOwned {
854
    pub fn to_shared<'a>(&'a self) -> FlexBasisParseError<'a> {
855
        match self {
856
            FlexBasisParseErrorOwned::InvalidValue(s) => {
857
                FlexBasisParseError::InvalidValue(s.as_str())
858
            }
859
        }
860
    }
861
}
862

            
863
#[cfg(feature = "parser")]
864
28
pub fn parse_layout_flex_basis<'a>(
865
28
    input: &'a str,
866
28
) -> Result<LayoutFlexBasis, FlexBasisParseError<'a>> {
867
    use crate::props::basic::pixel::parse_pixel_value;
868

            
869
28
    match input.trim() {
870
28
        "auto" => Ok(LayoutFlexBasis::Auto),
871
13
        s => parse_pixel_value(s)
872
13
            .map(LayoutFlexBasis::Exact)
873
13
            .map_err(|_| FlexBasisParseError::InvalidValue(input)),
874
    }
875
28
}
876

            
877
#[cfg(all(test, feature = "parser"))]
878
mod tests {
879
    use super::*;
880
    use crate::props::basic::pixel::PixelValue;
881

            
882
    #[test]
883
1
    fn test_parse_layout_flex_grow() {
884
1
        assert_eq!(parse_layout_flex_grow("0").unwrap().inner.get(), 0.0);
885
1
        assert_eq!(parse_layout_flex_grow("1").unwrap().inner.get(), 1.0);
886
1
        assert_eq!(parse_layout_flex_grow("2.5").unwrap().inner.get(), 2.5);
887
1
        assert_eq!(parse_layout_flex_grow("  0.5  ").unwrap().inner.get(), 0.5);
888
1
        assert!(parse_layout_flex_grow("none").is_err());
889
1
        assert!(parse_layout_flex_grow("-1").is_err()); // Negative values are invalid
890
1
    }
891

            
892
    #[test]
893
1
    fn test_parse_layout_flex_shrink() {
894
1
        assert_eq!(parse_layout_flex_shrink("0").unwrap().inner.get(), 0.0);
895
1
        assert_eq!(parse_layout_flex_shrink("1").unwrap().inner.get(), 1.0);
896
1
        assert_eq!(parse_layout_flex_shrink("3.0").unwrap().inner.get(), 3.0);
897
1
        assert_eq!(parse_layout_flex_shrink(" 0.2 ").unwrap().inner.get(), 0.2);
898
1
        assert!(parse_layout_flex_shrink("auto").is_err());
899
1
        assert!(parse_layout_flex_shrink("-1").is_err()); // Negative values are invalid
900
1
    }
901

            
902
    #[test]
903
1
    fn test_parse_layout_flex_direction() {
904
1
        assert_eq!(
905
1
            parse_layout_flex_direction("row").unwrap(),
906
            LayoutFlexDirection::Row
907
        );
908
1
        assert_eq!(
909
1
            parse_layout_flex_direction("row-reverse").unwrap(),
910
            LayoutFlexDirection::RowReverse
911
        );
912
1
        assert_eq!(
913
1
            parse_layout_flex_direction("column").unwrap(),
914
            LayoutFlexDirection::Column
915
        );
916
1
        assert_eq!(
917
1
            parse_layout_flex_direction("column-reverse").unwrap(),
918
            LayoutFlexDirection::ColumnReverse
919
        );
920
1
        assert_eq!(
921
1
            parse_layout_flex_direction("  row  ").unwrap(),
922
            LayoutFlexDirection::Row
923
        );
924
1
        assert!(parse_layout_flex_direction("reversed-row").is_err());
925
1
    }
926

            
927
    #[test]
928
1
    fn test_parse_layout_flex_wrap() {
929
1
        assert_eq!(
930
1
            parse_layout_flex_wrap("nowrap").unwrap(),
931
            LayoutFlexWrap::NoWrap
932
        );
933
1
        assert_eq!(
934
1
            parse_layout_flex_wrap("wrap").unwrap(),
935
            LayoutFlexWrap::Wrap
936
        );
937
1
        assert_eq!(
938
1
            parse_layout_flex_wrap("wrap-reverse").unwrap(),
939
            LayoutFlexWrap::WrapReverse
940
        );
941
1
        assert_eq!(
942
1
            parse_layout_flex_wrap("  wrap  ").unwrap(),
943
            LayoutFlexWrap::Wrap
944
        );
945
1
        assert!(parse_layout_flex_wrap("wrap reverse").is_err());
946
1
    }
947

            
948
    #[test]
949
1
    fn test_parse_layout_justify_content() {
950
1
        assert_eq!(
951
1
            parse_layout_justify_content("flex-start").unwrap(),
952
            LayoutJustifyContent::FlexStart
953
        );
954
1
        assert_eq!(
955
1
            parse_layout_justify_content("flex-end").unwrap(),
956
            LayoutJustifyContent::FlexEnd
957
        );
958
1
        assert_eq!(
959
1
            parse_layout_justify_content("start").unwrap(),
960
            LayoutJustifyContent::Start
961
        );
962
1
        assert_eq!(
963
1
            parse_layout_justify_content("end").unwrap(),
964
            LayoutJustifyContent::End
965
        );
966
1
        assert_eq!(
967
1
            parse_layout_justify_content("center").unwrap(),
968
            LayoutJustifyContent::Center
969
        );
970
1
        assert_eq!(
971
1
            parse_layout_justify_content("space-between").unwrap(),
972
            LayoutJustifyContent::SpaceBetween
973
        );
974
1
        assert_eq!(
975
1
            parse_layout_justify_content("space-around").unwrap(),
976
            LayoutJustifyContent::SpaceAround
977
        );
978
1
        assert_eq!(
979
1
            parse_layout_justify_content("space-evenly").unwrap(),
980
            LayoutJustifyContent::SpaceEvenly
981
        );
982
1
        assert_eq!(
983
1
            parse_layout_justify_content("  center  ").unwrap(),
984
            LayoutJustifyContent::Center
985
        );
986
1
    }
987

            
988
    #[test]
989
1
    fn test_parse_layout_align_items() {
990
1
        assert_eq!(
991
1
            parse_layout_align_items("stretch").unwrap(),
992
            LayoutAlignItems::Stretch
993
        );
994
1
        assert_eq!(
995
1
            parse_layout_align_items("flex-start").unwrap(),
996
            LayoutAlignItems::Start
997
        );
998
1
        assert_eq!(
999
1
            parse_layout_align_items("flex-end").unwrap(),
            LayoutAlignItems::End
        );
1
        assert_eq!(
1
            parse_layout_align_items("start").unwrap(),
            LayoutAlignItems::Start
        );
1
        assert_eq!(
1
            parse_layout_align_items("end").unwrap(),
            LayoutAlignItems::End
        );
1
        assert_eq!(
1
            parse_layout_align_items("center").unwrap(),
            LayoutAlignItems::Center
        );
1
        assert_eq!(
1
            parse_layout_align_items("baseline").unwrap(),
            LayoutAlignItems::Baseline
        );
1
        assert!(parse_layout_align_items("invalid").is_err());
1
    }
    #[test]
1
    fn test_parse_layout_align_content() {
1
        assert_eq!(
1
            parse_layout_align_content("stretch").unwrap(),
            LayoutAlignContent::Stretch
        );
1
        assert_eq!(
1
            parse_layout_align_content("flex-start").unwrap(),
            LayoutAlignContent::Start
        );
1
        assert_eq!(
1
            parse_layout_align_content("flex-end").unwrap(),
            LayoutAlignContent::End
        );
1
        assert_eq!(
1
            parse_layout_align_content("center").unwrap(),
            LayoutAlignContent::Center
        );
1
        assert_eq!(
1
            parse_layout_align_content("space-between").unwrap(),
            LayoutAlignContent::SpaceBetween
        );
1
        assert_eq!(
1
            parse_layout_align_content("space-around").unwrap(),
            LayoutAlignContent::SpaceAround
        );
1
        assert!(parse_layout_align_content("space-evenly").is_err()); // Not valid for align-content
1
    }
    #[test]
1
    fn test_parse_layout_flex_basis() {
1
        assert_eq!(
1
            parse_layout_flex_basis("auto").unwrap(),
            LayoutFlexBasis::Auto
        );
1
        assert_eq!(
1
            parse_layout_flex_basis("200px").unwrap(),
1
            LayoutFlexBasis::Exact(PixelValue::px(200.0))
        );
1
        assert_eq!(
1
            parse_layout_flex_basis("50%").unwrap(),
1
            LayoutFlexBasis::Exact(PixelValue::percent(50.0))
        );
1
        assert_eq!(
1
            parse_layout_flex_basis("  10em  ").unwrap(),
1
            LayoutFlexBasis::Exact(PixelValue::em(10.0))
        );
1
        assert!(parse_layout_flex_basis("none").is_err());
        // Liberal parsing accepts unitless numbers (treated as px)
1
        assert_eq!(
1
            parse_layout_flex_basis("200").unwrap(),
1
            LayoutFlexBasis::Exact(PixelValue::px(200.0))
        );
1
        assert_eq!(
1
            parse_layout_flex_basis("0").unwrap(),
1
            LayoutFlexBasis::Exact(PixelValue::px(0.0))
        );
1
    }
}