css_ast/types/
single_transition.rs

1use css_parse::{Cursor, Parse, Parser, Peek, Result as ParserResult, parse_optionals};
2use csskit_derives::{ToCursors, ToSpan, Visitable};
3
4use crate::{EasingFunction, NoneOr, SingleTransitionProperty, Time, TransitionBehaviorValue};
5
6// https://drafts.csswg.org/css-transitions-2/#single-transition
7// <single-transition> = [ none | <single-transition-property> ] || <time> || <easing-function> || <time> || <transition-behavior-value>
8#[derive(ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
10#[visit]
11pub struct SingleTransition<'a> {
12	#[visit(skip)]
13	pub property: Option<NoneOr<SingleTransitionProperty>>,
14	pub duration: Option<Time>,
15	pub easing: Option<EasingFunction<'a>>,
16	pub delay: Option<Time>,
17	#[visit(skip)]
18	pub behavior: Option<TransitionBehaviorValue>,
19}
20
21impl<'a> Peek<'a> for SingleTransition<'a> {
22	fn peek(p: &Parser<'a>, c: Cursor) -> bool {
23		<NoneOr<SingleTransitionProperty>>::peek(p, c) || EasingFunction::peek(p, c) || Time::peek(p, c)
24	}
25}
26
27impl<'a> Parse<'a> for SingleTransition<'a> {
28	fn parse(p: &mut Parser<'a>) -> ParserResult<Self> {
29		let (easing, property, duration, delay, behavior) = parse_optionals!(p, easing: EasingFunction, property: NoneOr<SingleTransitionProperty>, duration: Time, delay: Time, behavior: TransitionBehaviorValue);
30		Ok(Self { easing, property, duration, delay, behavior })
31	}
32}
33
34#[cfg(test)]
35mod tests {
36	use super::*;
37	use crate::assert_visits;
38	use css_parse::{assert_parse, assert_parse_error};
39
40	type NoneOrSingleTransitionProperty = NoneOr<SingleTransitionProperty>;
41
42	#[test]
43	fn size_test() {
44		assert_eq!(std::mem::size_of::<SingleTransition>(), 192);
45	}
46
47	#[test]
48	fn test_writes() {
49		assert_parse!(NoneOrSingleTransitionProperty, "none", NoneOrSingleTransitionProperty::None(_));
50		assert_parse!(NoneOrSingleTransitionProperty, "all", NoneOrSingleTransitionProperty::Some(_));
51
52		assert_parse!(SingleTransition, "none");
53		assert_parse!(SingleTransition, "opacity");
54		assert_parse!(SingleTransition, "opacity 1s");
55		assert_parse!(SingleTransition, "opacity 1s ease-in");
56		assert_parse!(SingleTransition, "opacity 1s ease-in 2s");
57		assert_parse!(SingleTransition, "2s ease-in");
58		assert_parse!(SingleTransition, "1s opacity", "opacity 1s");
59		assert_parse!(SingleTransition, "ease-in 1s opacity", "opacity 1s ease-in");
60		assert_parse!(SingleTransition, "1s 2s ease-in opacity", "opacity 1s ease-in 2s");
61		assert_parse!(SingleTransition, "ease-in opacity 1s 2s", "opacity 1s ease-in 2s");
62		assert_parse!(SingleTransition, "ease-in");
63		assert_parse!(SingleTransition, "1s");
64		assert_parse!(SingleTransition, "1s 2s");
65		assert_parse!(SingleTransition, "all 1s ease-in 2s");
66		assert_parse!(SingleTransition, "none 1s");
67		assert_parse!(SingleTransition, "none 1s normal");
68		assert_parse!(SingleTransition, "1s opacity allow-discrete", "opacity 1s allow-discrete");
69	}
70
71	#[test]
72	fn test_errors() {
73		assert_parse_error!(SingleTransition, "1deg");
74		assert_parse_error!(SingleTransition, "none none");
75	}
76
77	#[test]
78	fn test_visits() {
79		assert_visits!("1s", SingleTransition, Time);
80		assert_visits!("ease-in", SingleTransition, EasingFunction);
81		assert_visits!("1s 2s", SingleTransition, Time, Time);
82		assert_visits!("1s ease-in 2s", SingleTransition, Time, EasingFunction, Time);
83	}
84}