1use super::prelude::*;
2use crate::{Color, UrlOrString};
3
4#[node]
12#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
14#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(all))]
15#[derive(csskit_derives::NodeWithMetadata)]
16pub struct ImageFunction<'a> {
17 #[atom(CssAtomSet::Image)]
18 #[cfg_attr(feature = "visitable", visit(skip))]
19 pub name: T![Function],
20 pub params: ImageFunctionParams<'a>,
21 #[cfg_attr(feature = "visitable", visit(skip))]
22 #[semantic_eq(skip)]
23 pub close: T![')'],
24}
25
26#[node]
35#[derive(ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
37#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
38#[derive(csskit_derives::NodeWithMetadata)]
39pub struct ImageFunctionParams<'a> {
40 pub tags: Option<ImageTags>,
41 pub src: Option<UrlOrString>,
42 #[cfg_attr(feature = "visitable", visit(skip))]
43 #[semantic_eq(skip)]
44 pub comma: Option<T![,]>,
45 pub color: Option<Color<'a>>,
46}
47
48impl<'a> Peek<'a> for ImageFunctionParams<'a> {
49 const PEEK_KINDSET: KindSet =
50 ImageTags::PEEK_KINDSET.combine(UrlOrString::PEEK_KINDSET).combine(Color::PEEK_KINDSET);
51
52 #[inline(always)]
53 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
54 where
55 I: Iterator<Item = Cursor> + Clone,
56 {
57 ImageTags::peek(p, c) || UrlOrString::peek(p, c) || Color::peek(p, c)
58 }
59}
60
61impl<'a> Parse<'a> for ImageFunctionParams<'a> {
62 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
63 where
64 I: Iterator<Item = Cursor> + Clone,
65 {
66 let tags = p.parse_if_peek::<ImageTags>()?;
67 let src = p.parse_if_peek::<UrlOrString>()?;
68 let (comma, color) = if src.is_some() {
70 match p.parse_if_peek::<T![,]>()? {
71 Some(comma) => (Some(comma), Some(p.parse::<Color>()?)),
72 None => (None, None),
73 }
74 } else {
75 (None, Some(p.parse::<Color>()?))
76 };
77 Ok(Self { tags, src, comma, color })
78 }
79}
80
81#[node]
87#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
89#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
90#[derive(csskit_derives::NodeWithMetadata)]
91pub enum ImageTags {
92 #[atom(CssAtomSet::Ltr)]
93 Ltr(T![Ident]),
94 #[atom(CssAtomSet::Rtl)]
95 Rtl(T![Ident]),
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use crate::CssAtomSet;
102 use css_parse::{assert_parse, assert_parse_error};
103
104 #[test]
105 fn test_writes() {
106 assert_parse!(CssAtomSet::ATOMS, ImageFunction, "image(url(foo))");
107 assert_parse!(CssAtomSet::ATOMS, ImageFunction, "image('foo.png')");
108 assert_parse!(CssAtomSet::ATOMS, ImageFunction, "image(red)");
109 assert_parse!(CssAtomSet::ATOMS, ImageFunction, "image(url(foo),red)");
110 assert_parse!(CssAtomSet::ATOMS, ImageFunction, "image(ltr url(foo))");
111 assert_parse!(CssAtomSet::ATOMS, ImageFunction, "image(rtl 'foo.png',rgb(0 0 0))");
112 }
113
114 #[test]
115 fn test_errors() {
116 assert_parse_error!(CssAtomSet::ATOMS, ImageFunction, "image()");
117 assert_parse_error!(CssAtomSet::ATOMS, ImageFunction, "image(ltr)");
118 assert_parse_error!(CssAtomSet::ATOMS, ImageFunction, "image(url(foo),)");
119 assert_parse_error!(CssAtomSet::ATOMS, ImageFunction, "image(,red)");
120 assert_parse_error!(CssAtomSet::ATOMS, ImageFunction, "image(url(foo)red)");
121 }
122
123 #[test]
124 #[cfg(feature = "visitable")]
125 fn test_visits() {
126 use crate::assert_visits;
127 assert_visits!("image(url(foo),red)", ImageFunction, UrlOrString, Color);
128 }
129}