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