Skip to main content

css_ast/selector/
namespace.rs

1use super::prelude::*;
2
3use super::Tag;
4
5/// <https://drafts.csswg.org/selectors/#type-nmsp>
6#[node]
7#[derive(Peek, ToSpan, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
9#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
10#[derive(csskit_derives::NodeWithMetadata)]
11pub struct Namespace {
12	pub prefix: Option<NamespacePrefix>,
13	pub tag: NamespaceTag,
14}
15
16impl<'a> Parse<'a> for Namespace {
17	fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
18	where
19		I: Iterator<Item = Cursor> + Clone,
20	{
21		if p.peek::<T![*|]>() {
22			let prefix = p.parse::<NamespacePrefix>()?;
23			let tag = p.parse::<NamespaceTag>()?;
24			return Ok(Self { prefix: Some(prefix), tag });
25		}
26		if p.peek::<T![|]>() {
27			let prefix = p.parse::<NamespacePrefix>()?;
28			let tag = p.parse::<NamespaceTag>()?;
29			return Ok(Self { prefix: Some(prefix), tag });
30		}
31
32		let ident = p.parse::<T![Ident]>()?;
33		let skip = p.set_skip(KindSet::NONE);
34		if p.peek::<T![|]>() && !p.peek::<T![|=]>() {
35			let pipe = p.parse::<T![|]>();
36			let tag = p.parse::<NamespaceTag>();
37			p.set_skip(skip);
38			let prefix = NamespacePrefix::Name(ident, pipe?);
39			return Ok(Self { prefix: Some(prefix), tag: tag? });
40		}
41		let tag = p.parse::<NamespaceTag>()?;
42		Ok(Self { prefix: None, tag })
43	}
44}
45
46#[node]
47#[derive(Peek, ToSpan, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
49#[derive(csskit_derives::NodeWithMetadata)]
50pub enum NamespacePrefix {
51	None(#[semantic_eq(skip)] T![|]),
52	Name(T![Ident], #[semantic_eq(skip)] T![|]),
53	Wildcard(T![*], #[semantic_eq(skip)] T![|]),
54}
55
56impl<'a> Parse<'a> for NamespacePrefix {
57	fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
58	where
59		I: Iterator<Item = Cursor> + Clone,
60	{
61		if p.peek::<T![|]>() {
62			let pipe = p.parse::<T![|]>()?;
63			Ok(Self::None(pipe))
64		} else if p.peek::<T![*]>() {
65			let star = p.parse::<T![*]>()?;
66			let skip = p.set_skip(KindSet::NONE);
67			let pipe = p.parse::<T![|]>();
68			p.set_skip(skip);
69			let pipe = pipe?;
70			Ok(Self::Wildcard(star, pipe))
71		} else {
72			let star = p.parse::<T![Ident]>()?;
73			let skip = p.set_skip(KindSet::NONE);
74			let pipe = p.parse::<T![|]>();
75			p.set_skip(skip);
76			let pipe = pipe?;
77			Ok(Self::Name(star, pipe))
78		}
79	}
80}
81
82#[node]
83#[derive(Peek, ToCursors, IntoCursor, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
84#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
85#[derive(csskit_derives::NodeWithMetadata)]
86pub enum NamespaceTag {
87	Wildcard(#[semantic_eq(skip)] T![*]),
88	Tag(Tag),
89}
90
91impl<'a> Parse<'a> for NamespaceTag {
92	fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
93	where
94		I: Iterator<Item = Cursor> + Clone,
95	{
96		if p.peek::<Self>() {
97			if p.peek::<T![*]>() { Ok(Self::Wildcard(p.parse::<T![*]>()?)) } else { Ok(Self::Tag(p.parse::<Tag>()?)) }
98		} else {
99			Err(Diagnostic::new(p.next(), Diagnostic::unexpected))?
100		}
101	}
102}
103
104#[cfg(test)]
105mod tests {
106	use super::*;
107	use crate::CssAtomSet;
108	use css_parse::{assert_parse, assert_parse_error};
109
110	#[test]
111	fn test_writes() {
112		assert_parse!(CssAtomSet::ATOMS, Namespace, "*|a");
113		assert_parse!(CssAtomSet::ATOMS, Namespace, "html|div");
114		assert_parse!(CssAtomSet::ATOMS, Namespace, "|span");
115	}
116
117	#[test]
118	fn test_errors() {
119		assert_parse_error!(CssAtomSet::ATOMS, Namespace, "* | a");
120	}
121}