1use crate::{Syntax, diagnostics};
2use css_parse::{
3 ComponentValues, Cursor, Function, Parse, Parser, Peek, Result as ParserResult, T, function_set, keyword_set,
4};
5use csskit_derives::{Parse, Peek, ToCursors, ToSpan, Visitable};
6
7function_set!(pub struct AttrFunctionName "attr");
8
9#[derive(Parse, Peek, ToSpan, ToCursors, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
17#[visit(self)]
18pub struct AttrFunction<'a>(Function<AttrFunctionName, AttrFunctionParams<'a>>);
19
20#[derive(Parse, Peek, ToSpan, ToCursors, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
22pub struct AttrFunctionParams<'a>(AttrName, Option<AttrType>, Option<T![,]>, Option<ComponentValues<'a>>);
23
24#[derive(ToSpan, ToCursors, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
27pub struct AttrName(pub Option<T![Ident]>, pub Option<T![|]>, pub Option<T![Ident]>);
28
29impl<'a> Peek<'a> for AttrName {
30 fn peek(p: &Parser<'a>, c: Cursor) -> bool {
31 <T![Ident]>::peek(p, c)
32 }
33}
34
35impl<'a> Parse<'a> for AttrName {
36 fn parse(p: &mut Parser<'a>) -> ParserResult<Self> {
37 let a = p.parse_if_peek::<T![Ident]>()?;
38 let b = p.parse_if_peek::<T![|]>()?;
39
40 if a.is_some() && b.is_none() {
41 return Ok(Self(None, None, a));
42 }
43
44 if a.is_none() && b.is_some() {
45 return Ok(Self(None, b, Some(p.parse::<T![Ident]>()?)));
46 }
47
48 if a.is_none() && b.is_none() {
49 Err(diagnostics::ExpectedIdent(p.next()))?
50 }
51
52 debug_assert!(a.is_some() && b.is_some());
53
54 Ok(Self(a, b, Some(p.parse::<T![Ident]>()?)))
55 }
56}
57
58keyword_set!(pub struct AttrTypeKeywords "raw-string");
59
60function_set!(pub struct AttrTypeFunctionName "type");
61
62#[derive(ToSpan, ToCursors, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
64#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
65pub enum AttrType {
66 Type(Function<AttrTypeFunctionName, Syntax>),
67 RawString(T![Ident]),
68 Unit(T![DimensionIdent]),
69}
70
71impl<'a> Peek<'a> for AttrType {
72 fn peek(p: &Parser<'a>, c: Cursor) -> bool {
73 AttrTypeKeywords::peek(p, c) || <T![DimensionIdent]>::peek(p, c) || AttrTypeFunctionName::peek(p, c)
74 }
75}
76
77impl<'a> Parse<'a> for AttrType {
78 fn parse(p: &mut Parser<'a>) -> ParserResult<Self> {
79 if let Some(raw) = p.parse_if_peek::<AttrTypeKeywords>()? {
80 return Ok(Self::RawString(raw.into()));
81 }
82 if let Some(unit) = p.parse_if_peek::<T![DimensionIdent]>()? {
83 return Ok(Self::Unit(unit));
84 }
85 p.parse::<Function<AttrTypeFunctionName, Syntax>>().map(Self::Type)
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use css_parse::{assert_parse, assert_parse_error};
93
94 #[test]
95 fn size_test() {
96 assert_eq!(std::mem::size_of::<AttrFunction>(), 160);
97 }
98
99 #[test]
100 fn test_writes() {
101 assert_parse!(AttrFunction, "attr(foo)");
102 assert_parse!(AttrFunction, "attr(foo)");
103 assert_parse!(AttrFunction, "attr(bar px)");
104 assert_parse!(AttrFunction, "attr(foo|bar px)");
105 assert_parse!(AttrFunction, "attr(foo|bar)");
106 assert_parse!(AttrFunction, "attr(|bar)");
107 assert_parse!(AttrFunction, "attr(|bar px)");
108 }
109
110 #[test]
111 fn test_errors() {
112 assert_parse_error!(AttrName, "a|b|c");
113 assert_parse_error!(AttrFunction, "attrr(foo)");
114 assert_parse_error!(AttrFunction, "attr()");
115 assert_parse_error!(AttrFunction, "attr(|)");
116 }
117}