1#[cfg(feature = "parse")]
12use crate::util::parse::{CssParseResult, FromCss, Parser, Token};
13
14use crate::style::Direction;
15
16#[derive(Copy, Clone, PartialEq, Eq, Debug)]
22#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23#[repr(u8)]
24pub enum AlignItemsKeyword {
25 Start,
27 End,
29 FlexStart,
34 FlexEnd,
39 SelfStart,
45 SelfEnd,
51 Center,
53 Baseline,
55 Stretch,
57}
58
59#[derive(Copy, Clone, PartialEq, Eq, Debug)]
64#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
65#[repr(u8)]
66pub enum AlignContentKeyword {
67 Start,
69 End,
71 FlexStart,
73 FlexEnd,
75 Center,
77 Stretch,
79 SpaceBetween,
82 SpaceEvenly,
85 SpaceAround,
88}
89
90impl AlignContentKeyword {
91 pub(crate) fn reversed(self) -> Self {
97 match self {
98 Self::Start => Self::End,
99 Self::End => Self::Start,
100 Self::FlexStart => Self::FlexEnd,
101 Self::FlexEnd => Self::FlexStart,
102 Self::Stretch => Self::End,
103 Self::Center | Self::SpaceBetween | Self::SpaceEvenly | Self::SpaceAround => self,
104 }
105 }
106}
107
108#[derive(Copy, Clone, PartialEq, Eq, Debug)]
123#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
124#[repr(u8)]
125pub enum AlignmentSafety {
126 Unsafe,
129 Safe,
132}
133
134#[derive(Copy, Clone, PartialEq, Eq, Debug)]
140pub struct AlignItems {
141 pub keyword: AlignItemsKeyword,
143 pub safety: AlignmentSafety,
145}
146
147impl AlignItems {
148 pub const START: Self = Self { keyword: AlignItemsKeyword::Start, safety: AlignmentSafety::Unsafe };
150 pub const END: Self = Self { keyword: AlignItemsKeyword::End, safety: AlignmentSafety::Unsafe };
152 pub const FLEX_START: Self = Self { keyword: AlignItemsKeyword::FlexStart, safety: AlignmentSafety::Unsafe };
154 pub const FLEX_END: Self = Self { keyword: AlignItemsKeyword::FlexEnd, safety: AlignmentSafety::Unsafe };
156 pub const SELF_START: Self = Self { keyword: AlignItemsKeyword::SelfStart, safety: AlignmentSafety::Unsafe };
158 pub const SELF_END: Self = Self { keyword: AlignItemsKeyword::SelfEnd, safety: AlignmentSafety::Unsafe };
160 pub const CENTER: Self = Self { keyword: AlignItemsKeyword::Center, safety: AlignmentSafety::Unsafe };
162 pub const BASELINE: Self = Self { keyword: AlignItemsKeyword::Baseline, safety: AlignmentSafety::Unsafe };
164 pub const STRETCH: Self = Self { keyword: AlignItemsKeyword::Stretch, safety: AlignmentSafety::Unsafe };
166 pub const SAFE_START: Self = Self { keyword: AlignItemsKeyword::Start, safety: AlignmentSafety::Safe };
169 pub const SAFE_END: Self = Self { keyword: AlignItemsKeyword::End, safety: AlignmentSafety::Safe };
172 pub const SAFE_FLEX_START: Self = Self { keyword: AlignItemsKeyword::FlexStart, safety: AlignmentSafety::Safe };
175 pub const SAFE_FLEX_END: Self = Self { keyword: AlignItemsKeyword::FlexEnd, safety: AlignmentSafety::Safe };
178 pub const SAFE_CENTER: Self = Self { keyword: AlignItemsKeyword::Center, safety: AlignmentSafety::Safe };
181 pub const SAFE_SELF_START: Self = Self { keyword: AlignItemsKeyword::SelfStart, safety: AlignmentSafety::Safe };
184 pub const SAFE_SELF_END: Self = Self { keyword: AlignItemsKeyword::SelfEnd, safety: AlignmentSafety::Safe };
187
188 #[inline]
190 pub const fn is_safe(self) -> bool {
191 matches!(self.safety, AlignmentSafety::Safe)
192 }
193
194 #[inline]
196 pub const fn keyword(self) -> AlignItemsKeyword {
197 self.keyword
198 }
199
200 #[inline]
211 pub(crate) fn resolve_self_relative(
212 self,
213 item_direction: Direction,
214 container_direction: Direction,
215 axis_is_inline: bool,
216 ) -> Self {
217 let flip = axis_is_inline && item_direction != container_direction;
218 let keyword = match self.keyword {
219 AlignItemsKeyword::SelfStart => {
220 if flip {
221 AlignItemsKeyword::End
222 } else {
223 AlignItemsKeyword::Start
224 }
225 }
226 AlignItemsKeyword::SelfEnd => {
227 if flip {
228 AlignItemsKeyword::Start
229 } else {
230 AlignItemsKeyword::End
231 }
232 }
233 other => other,
234 };
235 Self { keyword, safety: self.safety }
236 }
237}
238
239#[cfg(feature = "parse")]
240impl FromCss for AlignItems {
241 fn from_css<'i>(input: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
242 let first = input.expect_ident()?.clone();
243 cssparser::match_ignore_ascii_case! { &*first,
244 "safe" => {
245 let pos = input.expect_ident()?.clone();
246 cssparser::match_ignore_ascii_case! { &*pos,
247 "start" => Ok(Self::SAFE_START),
248 "end" => Ok(Self::SAFE_END),
249 "flex-start" => Ok(Self::SAFE_FLEX_START),
250 "flex-end" => Ok(Self::SAFE_FLEX_END),
251 "self-start" => Ok(Self::SAFE_SELF_START),
252 "self-end" => Ok(Self::SAFE_SELF_END),
253 "center" => Ok(Self::SAFE_CENTER),
254 _ => Err(input.new_unexpected_token_error(Token::Ident(pos))),
255 }
256 },
257 "unsafe" => {
258 let pos = input.expect_ident()?.clone();
259 cssparser::match_ignore_ascii_case! { &*pos,
260 "start" => Ok(Self::START),
261 "end" => Ok(Self::END),
262 "flex-start" => Ok(Self::FLEX_START),
263 "flex-end" => Ok(Self::FLEX_END),
264 "self-start" => Ok(Self::SELF_START),
265 "self-end" => Ok(Self::SELF_END),
266 "center" => Ok(Self::CENTER),
267 _ => Err(input.new_unexpected_token_error(Token::Ident(pos))),
268 }
269 },
270 "start" => Ok(Self::START),
271 "end" => Ok(Self::END),
272 "flex-start" => Ok(Self::FLEX_START),
273 "flex-end" => Ok(Self::FLEX_END),
274 "self-start" => Ok(Self::SELF_START),
275 "self-end" => Ok(Self::SELF_END),
276 "center" => Ok(Self::CENTER),
277 "baseline" => Ok(Self::BASELINE),
278 "stretch" => Ok(Self::STRETCH),
279 _ => Err(input.new_unexpected_token_error(Token::Ident(first))),
280 }
281 }
282}
283
284#[cfg(feature = "parse")]
285crate::util::parse::from_str_from_css!(AlignItems);
286
287pub type JustifyItems = AlignItems;
293pub type AlignSelf = AlignItems;
301pub type JustifySelf = AlignItems;
309
310#[derive(Copy, Clone, PartialEq, Eq, Debug)]
316pub struct AlignContent {
317 pub keyword: AlignContentKeyword,
319 pub safety: AlignmentSafety,
321}
322
323impl AlignContent {
324 pub const START: Self = Self { keyword: AlignContentKeyword::Start, safety: AlignmentSafety::Unsafe };
326 pub const END: Self = Self { keyword: AlignContentKeyword::End, safety: AlignmentSafety::Unsafe };
328 pub const FLEX_START: Self = Self { keyword: AlignContentKeyword::FlexStart, safety: AlignmentSafety::Unsafe };
330 pub const FLEX_END: Self = Self { keyword: AlignContentKeyword::FlexEnd, safety: AlignmentSafety::Unsafe };
332 pub const CENTER: Self = Self { keyword: AlignContentKeyword::Center, safety: AlignmentSafety::Unsafe };
334 pub const STRETCH: Self = Self { keyword: AlignContentKeyword::Stretch, safety: AlignmentSafety::Unsafe };
336 pub const SPACE_BETWEEN: Self =
338 Self { keyword: AlignContentKeyword::SpaceBetween, safety: AlignmentSafety::Unsafe };
339 pub const SPACE_EVENLY: Self = Self { keyword: AlignContentKeyword::SpaceEvenly, safety: AlignmentSafety::Unsafe };
341 pub const SPACE_AROUND: Self = Self { keyword: AlignContentKeyword::SpaceAround, safety: AlignmentSafety::Unsafe };
343 pub const SAFE_START: Self = Self { keyword: AlignContentKeyword::Start, safety: AlignmentSafety::Safe };
346 pub const SAFE_END: Self = Self { keyword: AlignContentKeyword::End, safety: AlignmentSafety::Safe };
349 pub const SAFE_FLEX_START: Self = Self { keyword: AlignContentKeyword::FlexStart, safety: AlignmentSafety::Safe };
352 pub const SAFE_FLEX_END: Self = Self { keyword: AlignContentKeyword::FlexEnd, safety: AlignmentSafety::Safe };
355 pub const SAFE_CENTER: Self = Self { keyword: AlignContentKeyword::Center, safety: AlignmentSafety::Safe };
358
359 #[inline]
361 pub const fn is_safe(self) -> bool {
362 matches!(self.safety, AlignmentSafety::Safe)
363 }
364
365 #[inline]
367 pub const fn keyword(self) -> AlignContentKeyword {
368 self.keyword
369 }
370}
371
372#[cfg(feature = "parse")]
373impl FromCss for AlignContent {
374 fn from_css<'i>(input: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
375 let first = input.expect_ident()?.clone();
376 cssparser::match_ignore_ascii_case! { &*first,
377 "safe" => {
378 let pos = input.expect_ident()?.clone();
379 cssparser::match_ignore_ascii_case! { &*pos,
380 "start" => Ok(Self::SAFE_START),
381 "end" => Ok(Self::SAFE_END),
382 "flex-start" => Ok(Self::SAFE_FLEX_START),
383 "flex-end" => Ok(Self::SAFE_FLEX_END),
384 "center" => Ok(Self::SAFE_CENTER),
385 _ => Err(input.new_unexpected_token_error(Token::Ident(pos))),
386 }
387 },
388 "unsafe" => {
389 let pos = input.expect_ident()?.clone();
390 cssparser::match_ignore_ascii_case! { &*pos,
391 "start" => Ok(Self::START),
392 "end" => Ok(Self::END),
393 "flex-start" => Ok(Self::FLEX_START),
394 "flex-end" => Ok(Self::FLEX_END),
395 "center" => Ok(Self::CENTER),
396 _ => Err(input.new_unexpected_token_error(Token::Ident(pos))),
397 }
398 },
399 "start" => Ok(Self::START),
400 "end" => Ok(Self::END),
401 "flex-start" => Ok(Self::FLEX_START),
402 "flex-end" => Ok(Self::FLEX_END),
403 "center" => Ok(Self::CENTER),
404 "stretch" => Ok(Self::STRETCH),
405 "space-between" => Ok(Self::SPACE_BETWEEN),
406 "space-evenly" => Ok(Self::SPACE_EVENLY),
407 "space-around" => Ok(Self::SPACE_AROUND),
408 _ => Err(input.new_unexpected_token_error(Token::Ident(first))),
409 }
410 }
411}
412
413#[cfg(feature = "parse")]
414crate::util::parse::from_str_from_css!(AlignContent);
415
416pub type JustifyContent = AlignContent;
422
423#[cfg(feature = "serde")]
432const ALIGN_ITEMS_NAMES: &[&str] = &[
433 "Start",
434 "End",
435 "FlexStart",
436 "FlexEnd",
437 "SelfStart",
438 "SelfEnd",
439 "Center",
440 "Baseline",
441 "Stretch",
442 "SafeStart",
443 "SafeEnd",
444 "SafeFlexStart",
445 "SafeFlexEnd",
446 "SafeSelfStart",
447 "SafeSelfEnd",
448 "SafeCenter",
449];
450
451#[cfg(feature = "serde")]
452impl serde::Serialize for AlignItems {
453 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
454 let name = match (self.keyword, self.safety) {
455 (AlignItemsKeyword::Start, AlignmentSafety::Unsafe) => "Start",
456 (AlignItemsKeyword::End, AlignmentSafety::Unsafe) => "End",
457 (AlignItemsKeyword::FlexStart, AlignmentSafety::Unsafe) => "FlexStart",
458 (AlignItemsKeyword::FlexEnd, AlignmentSafety::Unsafe) => "FlexEnd",
459 (AlignItemsKeyword::SelfStart, AlignmentSafety::Unsafe) => "SelfStart",
460 (AlignItemsKeyword::SelfEnd, AlignmentSafety::Unsafe) => "SelfEnd",
461 (AlignItemsKeyword::Center, AlignmentSafety::Unsafe) => "Center",
462 (AlignItemsKeyword::Baseline, _) => "Baseline",
463 (AlignItemsKeyword::Stretch, _) => "Stretch",
464 (AlignItemsKeyword::Start, AlignmentSafety::Safe) => "SafeStart",
465 (AlignItemsKeyword::End, AlignmentSafety::Safe) => "SafeEnd",
466 (AlignItemsKeyword::FlexStart, AlignmentSafety::Safe) => "SafeFlexStart",
467 (AlignItemsKeyword::FlexEnd, AlignmentSafety::Safe) => "SafeFlexEnd",
468 (AlignItemsKeyword::SelfStart, AlignmentSafety::Safe) => "SafeSelfStart",
469 (AlignItemsKeyword::SelfEnd, AlignmentSafety::Safe) => "SafeSelfEnd",
470 (AlignItemsKeyword::Center, AlignmentSafety::Safe) => "SafeCenter",
471 };
472 serializer.serialize_str(name)
473 }
474}
475
476#[cfg(feature = "serde")]
477impl<'de> serde::Deserialize<'de> for AlignItems {
478 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
479 struct AlignItemsVisitor;
480 impl<'de> serde::de::Visitor<'de> for AlignItemsVisitor {
481 type Value = AlignItems;
482 fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
483 fmt.write_str("an AlignItems variant tag string")
484 }
485 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
486 Ok(match v {
487 "Start" => AlignItems::START,
488 "End" => AlignItems::END,
489 "FlexStart" => AlignItems::FLEX_START,
490 "FlexEnd" => AlignItems::FLEX_END,
491 "SelfStart" => AlignItems::SELF_START,
492 "SelfEnd" => AlignItems::SELF_END,
493 "Center" => AlignItems::CENTER,
494 "Baseline" => AlignItems::BASELINE,
495 "Stretch" => AlignItems::STRETCH,
496 "SafeStart" => AlignItems::SAFE_START,
497 "SafeEnd" => AlignItems::SAFE_END,
498 "SafeFlexStart" => AlignItems::SAFE_FLEX_START,
499 "SafeFlexEnd" => AlignItems::SAFE_FLEX_END,
500 "SafeSelfStart" => AlignItems::SAFE_SELF_START,
501 "SafeSelfEnd" => AlignItems::SAFE_SELF_END,
502 "SafeCenter" => AlignItems::SAFE_CENTER,
503 other => return Err(E::unknown_variant(other, ALIGN_ITEMS_NAMES)),
504 })
505 }
506 }
507 deserializer.deserialize_str(AlignItemsVisitor)
508 }
509}
510
511#[cfg(feature = "serde")]
514const ALIGN_CONTENT_NAMES: &[&str] = &[
515 "Start",
516 "End",
517 "FlexStart",
518 "FlexEnd",
519 "Center",
520 "Stretch",
521 "SpaceBetween",
522 "SpaceEvenly",
523 "SpaceAround",
524 "SafeStart",
525 "SafeEnd",
526 "SafeFlexStart",
527 "SafeFlexEnd",
528 "SafeCenter",
529];
530
531#[cfg(feature = "serde")]
532impl serde::Serialize for AlignContent {
533 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
534 let name = match (self.keyword, self.safety) {
535 (AlignContentKeyword::Start, AlignmentSafety::Unsafe) => "Start",
536 (AlignContentKeyword::End, AlignmentSafety::Unsafe) => "End",
537 (AlignContentKeyword::FlexStart, AlignmentSafety::Unsafe) => "FlexStart",
538 (AlignContentKeyword::FlexEnd, AlignmentSafety::Unsafe) => "FlexEnd",
539 (AlignContentKeyword::Center, AlignmentSafety::Unsafe) => "Center",
540 (AlignContentKeyword::Stretch, _) => "Stretch",
541 (AlignContentKeyword::SpaceBetween, _) => "SpaceBetween",
542 (AlignContentKeyword::SpaceEvenly, _) => "SpaceEvenly",
543 (AlignContentKeyword::SpaceAround, _) => "SpaceAround",
544 (AlignContentKeyword::Start, AlignmentSafety::Safe) => "SafeStart",
545 (AlignContentKeyword::End, AlignmentSafety::Safe) => "SafeEnd",
546 (AlignContentKeyword::FlexStart, AlignmentSafety::Safe) => "SafeFlexStart",
547 (AlignContentKeyword::FlexEnd, AlignmentSafety::Safe) => "SafeFlexEnd",
548 (AlignContentKeyword::Center, AlignmentSafety::Safe) => "SafeCenter",
549 };
550 serializer.serialize_str(name)
551 }
552}
553
554#[cfg(feature = "serde")]
555impl<'de> serde::Deserialize<'de> for AlignContent {
556 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
557 struct AlignContentVisitor;
558 impl<'de> serde::de::Visitor<'de> for AlignContentVisitor {
559 type Value = AlignContent;
560 fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
561 fmt.write_str("an AlignContent variant tag string")
562 }
563 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
564 Ok(match v {
565 "Start" => AlignContent::START,
566 "End" => AlignContent::END,
567 "FlexStart" => AlignContent::FLEX_START,
568 "FlexEnd" => AlignContent::FLEX_END,
569 "Center" => AlignContent::CENTER,
570 "Stretch" => AlignContent::STRETCH,
571 "SpaceBetween" => AlignContent::SPACE_BETWEEN,
572 "SpaceEvenly" => AlignContent::SPACE_EVENLY,
573 "SpaceAround" => AlignContent::SPACE_AROUND,
574 "SafeStart" => AlignContent::SAFE_START,
575 "SafeEnd" => AlignContent::SAFE_END,
576 "SafeFlexStart" => AlignContent::SAFE_FLEX_START,
577 "SafeFlexEnd" => AlignContent::SAFE_FLEX_END,
578 "SafeCenter" => AlignContent::SAFE_CENTER,
579 other => return Err(E::unknown_variant(other, ALIGN_CONTENT_NAMES)),
580 })
581 }
582 }
583 deserializer.deserialize_str(AlignContentVisitor)
584 }
585}
586
587#[cfg(test)]
588mod tests {
589 use super::*;
590 use core::mem::size_of;
591
592 #[test]
595 fn align_types_within_size_budget() {
596 assert!(size_of::<AlignItems>() <= 2, "AlignItems grew to {}", size_of::<AlignItems>());
597 assert!(size_of::<AlignContent>() <= 2, "AlignContent grew to {}", size_of::<AlignContent>());
598 assert!(size_of::<Option<AlignItems>>() <= 3);
599 assert!(size_of::<Option<AlignContent>>() <= 3);
600 }
601
602 #[test]
603 fn align_items_is_safe() {
604 assert!(AlignItems::SAFE_START.is_safe());
605 assert!(AlignItems::SAFE_END.is_safe());
606 assert!(AlignItems::SAFE_FLEX_START.is_safe());
607 assert!(AlignItems::SAFE_FLEX_END.is_safe());
608 assert!(AlignItems::SAFE_CENTER.is_safe());
609 assert!(!AlignItems::START.is_safe());
610 assert!(!AlignItems::END.is_safe());
611 assert!(!AlignItems::FLEX_START.is_safe());
612 assert!(!AlignItems::FLEX_END.is_safe());
613 assert!(!AlignItems::CENTER.is_safe());
614 assert!(!AlignItems::BASELINE.is_safe());
615 assert!(!AlignItems::STRETCH.is_safe());
616 assert!(AlignItems::SAFE_SELF_START.is_safe());
617 assert!(AlignItems::SAFE_SELF_END.is_safe());
618 assert!(!AlignItems::SELF_START.is_safe());
619 assert!(!AlignItems::SELF_END.is_safe());
620 }
621
622 #[test]
623 fn align_items_keyword_strips_safe() {
624 assert_eq!(AlignItems::SAFE_START.keyword(), AlignItemsKeyword::Start);
625 assert_eq!(AlignItems::SAFE_END.keyword(), AlignItemsKeyword::End);
626 assert_eq!(AlignItems::SAFE_FLEX_START.keyword(), AlignItemsKeyword::FlexStart);
627 assert_eq!(AlignItems::SAFE_FLEX_END.keyword(), AlignItemsKeyword::FlexEnd);
628 assert_eq!(AlignItems::SAFE_CENTER.keyword(), AlignItemsKeyword::Center);
629 assert_eq!(AlignItems::SAFE_SELF_START.keyword(), AlignItemsKeyword::SelfStart);
630 assert_eq!(AlignItems::SAFE_SELF_END.keyword(), AlignItemsKeyword::SelfEnd);
631 }
632
633 #[test]
634 fn align_items_keyword_passthrough() {
635 assert_eq!(AlignItems::START.keyword(), AlignItemsKeyword::Start);
636 assert_eq!(AlignItems::STRETCH.keyword(), AlignItemsKeyword::Stretch);
637 assert_eq!(AlignItems::BASELINE.keyword(), AlignItemsKeyword::Baseline);
638 assert_eq!(AlignItems::FLEX_START.keyword(), AlignItemsKeyword::FlexStart);
639 }
640
641 #[test]
642 fn resolve_self_relative_inline_axis() {
643 use Direction::{Ltr, Rtl};
644 assert_eq!(AlignItems::SELF_START.resolve_self_relative(Ltr, Ltr, true), AlignItems::START);
646 assert_eq!(AlignItems::SELF_END.resolve_self_relative(Ltr, Ltr, true), AlignItems::END);
647 assert_eq!(AlignItems::SELF_START.resolve_self_relative(Rtl, Rtl, true), AlignItems::START);
648 assert_eq!(AlignItems::SELF_END.resolve_self_relative(Rtl, Rtl, true), AlignItems::END);
649 assert_eq!(AlignItems::SELF_START.resolve_self_relative(Ltr, Rtl, true), AlignItems::END);
651 assert_eq!(AlignItems::SELF_END.resolve_self_relative(Ltr, Rtl, true), AlignItems::START);
652 assert_eq!(AlignItems::SELF_START.resolve_self_relative(Rtl, Ltr, true), AlignItems::END);
653 assert_eq!(AlignItems::SELF_END.resolve_self_relative(Rtl, Ltr, true), AlignItems::START);
654 assert_eq!(AlignItems::SAFE_SELF_START.resolve_self_relative(Ltr, Rtl, true), AlignItems::SAFE_END);
656 assert_eq!(AlignItems::START.resolve_self_relative(Ltr, Rtl, true), AlignItems::START);
658 assert_eq!(AlignItems::FLEX_END.resolve_self_relative(Ltr, Rtl, true), AlignItems::FLEX_END);
659 }
660
661 #[test]
662 fn resolve_self_relative_block_axis() {
663 use Direction::{Ltr, Rtl};
664 assert_eq!(AlignItems::SELF_START.resolve_self_relative(Ltr, Rtl, false), AlignItems::START);
666 assert_eq!(AlignItems::SELF_END.resolve_self_relative(Rtl, Ltr, false), AlignItems::END);
667 }
668
669 #[test]
670 fn align_content_is_safe() {
671 assert!(AlignContent::SAFE_START.is_safe());
672 assert!(AlignContent::SAFE_CENTER.is_safe());
673 assert!(!AlignContent::SPACE_BETWEEN.is_safe());
674 assert!(!AlignContent::STRETCH.is_safe());
675 }
676
677 #[test]
678 fn align_content_keyword_strips_safe() {
679 assert_eq!(AlignContent::SAFE_START.keyword(), AlignContentKeyword::Start);
680 assert_eq!(AlignContent::SAFE_FLEX_END.keyword(), AlignContentKeyword::FlexEnd);
681 assert_eq!(AlignContent::SAFE_CENTER.keyword(), AlignContentKeyword::Center);
682 assert_eq!(AlignContent::SPACE_BETWEEN.keyword(), AlignContentKeyword::SpaceBetween);
683 }
684
685 #[test]
686 fn align_content_keyword_reversed_swaps_start_end() {
687 assert_eq!(AlignContentKeyword::Start.reversed(), AlignContentKeyword::End);
688 assert_eq!(AlignContentKeyword::End.reversed(), AlignContentKeyword::Start);
689 assert_eq!(AlignContentKeyword::FlexStart.reversed(), AlignContentKeyword::FlexEnd);
690 assert_eq!(AlignContentKeyword::FlexEnd.reversed(), AlignContentKeyword::FlexStart);
691 assert_eq!(AlignContentKeyword::Stretch.reversed(), AlignContentKeyword::End);
693 assert_eq!(AlignContentKeyword::Center.reversed(), AlignContentKeyword::Center);
694 assert_eq!(AlignContentKeyword::SpaceBetween.reversed(), AlignContentKeyword::SpaceBetween);
695 assert_eq!(AlignContentKeyword::SpaceEvenly.reversed(), AlignContentKeyword::SpaceEvenly);
696 assert_eq!(AlignContentKeyword::SpaceAround.reversed(), AlignContentKeyword::SpaceAround);
697 }
698
699 #[cfg(feature = "parse")]
700 #[test]
701 fn parse_align_items_plain() {
702 assert_eq!("start".parse::<AlignItems>().unwrap(), AlignItems::START);
703 assert_eq!("end".parse::<AlignItems>().unwrap(), AlignItems::END);
704 assert_eq!("flex-start".parse::<AlignItems>().unwrap(), AlignItems::FLEX_START);
705 assert_eq!("flex-end".parse::<AlignItems>().unwrap(), AlignItems::FLEX_END);
706 assert_eq!("center".parse::<AlignItems>().unwrap(), AlignItems::CENTER);
707 assert_eq!("self-start".parse::<AlignItems>().unwrap(), AlignItems::SELF_START);
708 assert_eq!("self-end".parse::<AlignItems>().unwrap(), AlignItems::SELF_END);
709 assert_eq!("baseline".parse::<AlignItems>().unwrap(), AlignItems::BASELINE);
710 assert_eq!("stretch".parse::<AlignItems>().unwrap(), AlignItems::STRETCH);
711 }
712
713 #[cfg(feature = "parse")]
714 #[test]
715 fn parse_align_items_safe() {
716 assert_eq!("safe start".parse::<AlignItems>().unwrap(), AlignItems::SAFE_START);
717 assert_eq!("safe end".parse::<AlignItems>().unwrap(), AlignItems::SAFE_END);
718 assert_eq!("safe flex-start".parse::<AlignItems>().unwrap(), AlignItems::SAFE_FLEX_START);
719 assert_eq!("safe flex-end".parse::<AlignItems>().unwrap(), AlignItems::SAFE_FLEX_END);
720 assert_eq!("safe self-start".parse::<AlignItems>().unwrap(), AlignItems::SAFE_SELF_START);
721 assert_eq!("safe self-end".parse::<AlignItems>().unwrap(), AlignItems::SAFE_SELF_END);
722 assert_eq!("safe center".parse::<AlignItems>().unwrap(), AlignItems::SAFE_CENTER);
723 }
724
725 #[cfg(feature = "parse")]
726 #[test]
727 fn parse_align_items_safe_case_insensitive() {
728 assert_eq!("SAFE Start".parse::<AlignItems>().unwrap(), AlignItems::SAFE_START);
729 assert_eq!("Safe FLEX-end".parse::<AlignItems>().unwrap(), AlignItems::SAFE_FLEX_END);
730 }
731
732 #[cfg(feature = "parse")]
733 #[test]
734 fn parse_align_items_unsafe_drops_modifier() {
735 assert_eq!("unsafe start".parse::<AlignItems>().unwrap(), AlignItems::START);
736 assert_eq!("unsafe end".parse::<AlignItems>().unwrap(), AlignItems::END);
737 assert_eq!("unsafe self-start".parse::<AlignItems>().unwrap(), AlignItems::SELF_START);
738 assert_eq!("unsafe self-end".parse::<AlignItems>().unwrap(), AlignItems::SELF_END);
739 assert_eq!("unsafe center".parse::<AlignItems>().unwrap(), AlignItems::CENTER);
740 }
741
742 #[cfg(feature = "parse")]
743 #[test]
744 fn parse_align_items_rejects_invalid_safe_combos() {
745 assert!("safe stretch".parse::<AlignItems>().is_err());
746 assert!("safe baseline".parse::<AlignItems>().is_err());
747 assert!("safe space-between".parse::<AlignItems>().is_err());
748 assert!("safe".parse::<AlignItems>().is_err());
749 assert!("safe garbage".parse::<AlignItems>().is_err());
750 assert!("unsafe stretch".parse::<AlignItems>().is_err());
751 assert!("unsafe baseline".parse::<AlignItems>().is_err());
752 }
753
754 #[cfg(feature = "parse")]
755 #[test]
756 fn parse_align_content_plain() {
757 assert_eq!("start".parse::<AlignContent>().unwrap(), AlignContent::START);
758 assert_eq!("space-between".parse::<AlignContent>().unwrap(), AlignContent::SPACE_BETWEEN);
759 assert_eq!("space-evenly".parse::<AlignContent>().unwrap(), AlignContent::SPACE_EVENLY);
760 assert_eq!("space-around".parse::<AlignContent>().unwrap(), AlignContent::SPACE_AROUND);
761 assert_eq!("stretch".parse::<AlignContent>().unwrap(), AlignContent::STRETCH);
762 }
763
764 #[cfg(feature = "parse")]
765 #[test]
766 fn parse_align_content_safe() {
767 assert_eq!("safe start".parse::<AlignContent>().unwrap(), AlignContent::SAFE_START);
768 assert_eq!("safe end".parse::<AlignContent>().unwrap(), AlignContent::SAFE_END);
769 assert_eq!("safe flex-start".parse::<AlignContent>().unwrap(), AlignContent::SAFE_FLEX_START);
770 assert_eq!("safe flex-end".parse::<AlignContent>().unwrap(), AlignContent::SAFE_FLEX_END);
771 assert_eq!("safe center".parse::<AlignContent>().unwrap(), AlignContent::SAFE_CENTER);
772 }
773
774 #[cfg(feature = "parse")]
775 #[test]
776 fn parse_align_content_unsafe_drops_modifier() {
777 assert_eq!("unsafe start".parse::<AlignContent>().unwrap(), AlignContent::START);
778 assert_eq!("unsafe flex-end".parse::<AlignContent>().unwrap(), AlignContent::FLEX_END);
779 }
780
781 #[cfg(feature = "parse")]
782 #[test]
783 fn parse_align_content_rejects_invalid_safe_combos() {
784 assert!("safe stretch".parse::<AlignContent>().is_err());
785 assert!("safe space-between".parse::<AlignContent>().is_err());
786 assert!("safe space-evenly".parse::<AlignContent>().is_err());
787 assert!("safe space-around".parse::<AlignContent>().is_err());
788 assert!("safe".parse::<AlignContent>().is_err());
789 assert!("unsafe stretch".parse::<AlignContent>().is_err());
790 assert!("unsafe space-between".parse::<AlignContent>().is_err());
791 }
792
793 #[cfg(feature = "serde")]
794 #[test]
795 fn serde_align_items_round_trip() {
796 let cases = [
797 (AlignItems::START, "\"Start\""),
798 (AlignItems::END, "\"End\""),
799 (AlignItems::FLEX_START, "\"FlexStart\""),
800 (AlignItems::FLEX_END, "\"FlexEnd\""),
801 (AlignItems::CENTER, "\"Center\""),
802 (AlignItems::BASELINE, "\"Baseline\""),
803 (AlignItems::STRETCH, "\"Stretch\""),
804 (AlignItems::SAFE_START, "\"SafeStart\""),
805 (AlignItems::SAFE_END, "\"SafeEnd\""),
806 (AlignItems::SAFE_FLEX_START, "\"SafeFlexStart\""),
807 (AlignItems::SAFE_FLEX_END, "\"SafeFlexEnd\""),
808 (AlignItems::SAFE_CENTER, "\"SafeCenter\""),
809 (AlignItems::SELF_START, "\"SelfStart\""),
810 (AlignItems::SELF_END, "\"SelfEnd\""),
811 (AlignItems::SAFE_SELF_START, "\"SafeSelfStart\""),
812 (AlignItems::SAFE_SELF_END, "\"SafeSelfEnd\""),
813 ];
814 for (value, expected) in cases {
815 let serialized = serde_json::to_string(&value).unwrap();
816 assert_eq!(serialized, expected, "serialize {:?}", value);
817 let deserialized: AlignItems = serde_json::from_str(expected).unwrap();
818 assert_eq!(deserialized, value, "round-trip {:?}", value);
819 }
820 assert!(serde_json::from_str::<AlignItems>("\"NotAVariant\"").is_err());
821 }
822
823 #[cfg(feature = "serde")]
824 #[test]
825 fn serde_align_content_round_trip() {
826 let cases = [
827 (AlignContent::START, "\"Start\""),
828 (AlignContent::END, "\"End\""),
829 (AlignContent::FLEX_START, "\"FlexStart\""),
830 (AlignContent::FLEX_END, "\"FlexEnd\""),
831 (AlignContent::CENTER, "\"Center\""),
832 (AlignContent::STRETCH, "\"Stretch\""),
833 (AlignContent::SPACE_BETWEEN, "\"SpaceBetween\""),
834 (AlignContent::SPACE_EVENLY, "\"SpaceEvenly\""),
835 (AlignContent::SPACE_AROUND, "\"SpaceAround\""),
836 (AlignContent::SAFE_START, "\"SafeStart\""),
837 (AlignContent::SAFE_END, "\"SafeEnd\""),
838 (AlignContent::SAFE_FLEX_START, "\"SafeFlexStart\""),
839 (AlignContent::SAFE_FLEX_END, "\"SafeFlexEnd\""),
840 (AlignContent::SAFE_CENTER, "\"SafeCenter\""),
841 ];
842 for (value, expected) in cases {
843 let serialized = serde_json::to_string(&value).unwrap();
844 assert_eq!(serialized, expected, "serialize {:?}", value);
845 let deserialized: AlignContent = serde_json::from_str(expected).unwrap();
846 assert_eq!(deserialized, value, "round-trip {:?}", value);
847 }
848 assert!(serde_json::from_str::<AlignContent>("\"NotAVariant\"").is_err());
849 }
850}