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