1use super::prelude::*;
2use crate::{CalcableValue, Color, Image, Percentage, Ranged};
3
4#[node]
11#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
13#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(all))]
14#[derive(csskit_derives::NodeWithMetadata)]
15pub struct CrossFadeFunction<'a> {
16 #[atom(CssAtomSet::CrossFade)]
17 #[cfg_attr(feature = "visitable", visit(skip))]
18 pub name: T![Function],
19 pub params: CommaSeparated<'a, CfImage<'a>>,
20 #[cfg_attr(feature = "visitable", visit(skip))]
21 #[semantic_eq(skip)]
22 pub close: T![')'],
23}
24
25#[node]
33#[derive(ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
35#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
36#[derive(csskit_derives::NodeWithMetadata)]
37pub struct CfImage<'a> {
38 pub image: CfImageValue<'a>,
39 pub percentage: Option<CalcableValue<'a, Ranged<Percentage, 0, 100>>>,
40}
41
42impl<'a> Peek<'a> for CfImage<'a> {
43 const PEEK_KINDSET: KindSet =
44 CfImageValue::PEEK_KINDSET.combine(CalcableValue::<Ranged<Percentage, 0, 100>>::PEEK_KINDSET);
45
46 #[inline(always)]
47 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
48 where
49 I: Iterator<Item = Cursor> + Clone,
50 {
51 CfImageValue::peek(p, c) || CalcableValue::<Ranged<Percentage, 0, 100>>::peek(p, c)
52 }
53}
54
55impl<'a> Parse<'a> for CfImage<'a> {
56 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
57 where
58 I: Iterator<Item = Cursor> + Clone,
59 {
60 let mut image = p.parse_if_peek::<CfImageValue>()?;
62 let percentage = p.parse_if_peek::<CalcableValue<Ranged<Percentage, 0, 100>>>()?;
63 if image.is_none() {
64 image = Some(p.parse::<CfImageValue>()?);
65 }
66 Ok(Self { image: image.unwrap(), percentage })
67 }
68}
69
70#[node]
76#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
77#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
78#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
79#[derive(csskit_derives::NodeWithMetadata)]
80pub enum CfImageValue<'a> {
81 Image(Image<'a>),
82 Color(Color<'a>),
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use crate::CssAtomSet;
89 use css_parse::{assert_parse, assert_parse_error};
90
91 #[test]
92 fn test_writes() {
93 assert_parse!(CssAtomSet::ATOMS, CrossFadeFunction, "cross-fade(url(a))");
94 assert_parse!(CssAtomSet::ATOMS, CrossFadeFunction, "cross-fade(url(a)20%,url(b)80%)");
95 assert_parse!(CssAtomSet::ATOMS, CrossFadeFunction, "cross-fade(red 20%,blue)");
96 assert_parse!(CssAtomSet::ATOMS, CrossFadeFunction, "cross-fade(20% red,80% blue)");
97 assert_parse!(CssAtomSet::ATOMS, CrossFadeFunction, "cross-fade(linear-gradient(red,blue)50%,url(a))");
98 assert_parse!(CssAtomSet::ATOMS, CrossFadeFunction, "cross-fade(url(a)var(--pct))");
99 }
100
101 #[test]
102 fn test_errors() {
103 assert_parse_error!(CssAtomSet::ATOMS, CrossFadeFunction, "cross-fade()");
104 assert_parse_error!(CssAtomSet::ATOMS, CrossFadeFunction, "cross-fade(20%)");
105 assert_parse_error!(CssAtomSet::ATOMS, CrossFadeFunction, "cross-fade(red 120%)");
106 assert_parse_error!(CssAtomSet::ATOMS, CrossFadeFunction, "cross-fade(url(a),)");
107 }
108
109 #[test]
110 #[cfg(feature = "visitable")]
111 fn test_visits() {
112 use crate::assert_visits;
113 assert_visits!("cross-fade(red 20%,url(a))", CrossFadeFunction, Color, Percentage, Image, Url);
114 }
115}