1use super::prelude::*;
2use crate::{EasingFunction, NoneOr, SingleTransitionProperty, Time, TransitionBehaviorValue};
3use css_parse::parse_optionals;
4
5#[derive(ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
12#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
13#[derive(csskit_derives::NodeWithMetadata)]
14pub struct SingleTransition<'a> {
15 #[cfg_attr(feature = "visitable", visit(skip))]
16 pub property: Option<NoneOr<SingleTransitionProperty>>,
17 pub duration: Option<Time>,
18 pub easing: Option<EasingFunction<'a>>,
19 pub delay: Option<Time>,
20 #[cfg_attr(feature = "visitable", visit(skip))]
21 pub behavior: Option<TransitionBehaviorValue>,
22}
23
24impl<'a> Peek<'a> for SingleTransition<'a> {
25 const PEEK_KINDSET: KindSet = <NoneOr<SingleTransitionProperty>>::PEEK_KINDSET
26 .combine(EasingFunction::PEEK_KINDSET)
27 .combine(Time::PEEK_KINDSET);
28
29 #[inline(always)]
30 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
31 where
32 I: Iterator<Item = Cursor> + Clone,
33 {
34 <NoneOr<SingleTransitionProperty>>::peek(p, c) || EasingFunction::peek(p, c) || Time::peek(p, c)
35 }
36}
37
38impl<'a> Parse<'a> for SingleTransition<'a> {
39 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
40 where
41 I: Iterator<Item = Cursor> + Clone,
42 {
43 let (easing, property, duration, delay, behavior) = parse_optionals!(p, easing: EasingFunction, property: NoneOr<SingleTransitionProperty>, duration: Time, delay: Time, behavior: TransitionBehaviorValue);
44 Ok(Self { easing, property, duration, delay, behavior })
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51 use crate::CssAtomSet;
52 use css_parse::{assert_parse, assert_parse_error, assert_peek_false};
53
54 type NoneOrSingleTransitionProperty = NoneOr<SingleTransitionProperty>;
55
56 #[test]
57 fn size_test() {
58 assert_eq!(std::mem::size_of::<SingleTransition>(), 184);
59 }
60
61 #[test]
62 fn test_writes() {
63 assert_parse!(
64 CssAtomSet::ATOMS,
65 NoneOrSingleTransitionProperty,
66 "none",
67 NoneOrSingleTransitionProperty::None(_)
68 );
69 assert_parse!(
70 CssAtomSet::ATOMS,
71 NoneOrSingleTransitionProperty,
72 "all",
73 NoneOrSingleTransitionProperty::Some(_)
74 );
75
76 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "none");
77 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "opacity");
78 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "opacity 1s");
79 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "opacity 1s ease-in");
80 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "opacity 1s ease-in 2s");
81 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "2s ease-in");
82 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "1s opacity");
83 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "ease-in 1s opacity");
84 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "1s 2s ease-in opacity");
85 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "ease-in opacity 1s 2s");
86 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "ease-in");
87 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "1s");
88 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "1s 2s");
89 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "all 1s ease-in 2s");
90 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "none 1s");
91 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "none 1s normal");
92 assert_parse!(CssAtomSet::ATOMS, SingleTransition, "1s opacity allow-discrete");
93 }
94
95 #[test]
96 fn test_errors() {
97 assert_peek_false!(CssAtomSet::ATOMS, SingleTransition, "1deg");
98 assert_parse_error!(CssAtomSet::ATOMS, SingleTransition, "none none");
99 }
100
101 #[test]
102 #[cfg(feature = "visitable")]
103 fn test_visits() {
104 use crate::assert_visits;
105 assert_visits!("1s", SingleTransition, Time);
106 assert_visits!("ease-in", SingleTransition, EasingFunction);
107 assert_visits!("1s 2s", SingleTransition, Time, Time);
108 assert_visits!("1s ease-in 2s", SingleTransition, Time, EasingFunction, Time);
109 }
110}