1use super::prelude::*;
2use crate::NumericValue;
3
4#[node]
10#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
12#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
13#[derive(csskit_derives::NodeWithMetadata)]
14pub enum VoiceAge {
15 #[atom(CssAtomSet::Child)]
16 Child(T![Ident]),
17 #[atom(CssAtomSet::Young)]
18 Young(T![Ident]),
19 #[atom(CssAtomSet::Old)]
20 Old(T![Ident]),
21}
22
23#[node]
29#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
31#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
32#[derive(csskit_derives::NodeWithMetadata)]
33pub enum VoiceGender {
34 #[atom(CssAtomSet::Male)]
35 Male(T![Ident]),
36 #[atom(CssAtomSet::Female)]
37 Female(T![Ident]),
38 #[atom(CssAtomSet::Neutral)]
39 Neutral(T![Ident]),
40}
41
42#[node]
48#[derive(ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
50#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
51#[derive(csskit_derives::NodeWithMetadata)]
52pub struct GenericVoice<'a> {
53 pub age: Option<VoiceAge>,
54 pub gender: VoiceGender,
55 pub variant: Option<NumericValue<'a, T![Number]>>,
56}
57
58impl<'a> Peek<'a> for GenericVoice<'a> {
59 const PEEK_KINDSET: KindSet = VoiceAge::PEEK_KINDSET.combine(VoiceGender::PEEK_KINDSET);
60
61 #[inline(always)]
62 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
63 where
64 I: Iterator<Item = Cursor> + Clone,
65 {
66 VoiceAge::peek(p, c) || VoiceGender::peek(p, c)
67 }
68}
69
70impl<'a> Parse<'a> for GenericVoice<'a> {
71 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
72 where
73 I: Iterator<Item = Cursor> + Clone,
74 {
75 let age = p.parse_if_peek::<VoiceAge>()?;
76 let gender = p.parse::<VoiceGender>()?;
77 let variant = p.parse_if_peek::<NumericValue<T![Number]>>()?;
78 Ok(Self { age, gender, variant })
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use crate::CssAtomSet;
86 use css_parse::{assert_parse, assert_parse_error, assert_peek_false};
87
88 #[test]
89 fn test_parses() {
90 assert_parse!(CssAtomSet::ATOMS, GenericVoice, "male");
91 assert_parse!(CssAtomSet::ATOMS, GenericVoice, "female");
92 assert_parse!(CssAtomSet::ATOMS, GenericVoice, "neutral");
93 assert_parse!(CssAtomSet::ATOMS, GenericVoice, "child male");
94 assert_parse!(CssAtomSet::ATOMS, GenericVoice, "young female 2");
95 assert_parse!(CssAtomSet::ATOMS, GenericVoice, "old neutral 1");
96 }
97
98 #[test]
99 fn test_substitution() {
100 assert_parse!(CssAtomSet::ATOMS, GenericVoice, "male var(--n)");
101 assert_parse!(CssAtomSet::ATOMS, GenericVoice, "young female calc(1 + 1)");
102 }
103
104 #[test]
105 fn test_errors() {
106 assert_peek_false!(CssAtomSet::ATOMS, GenericVoice, "");
107 assert_parse_error!(CssAtomSet::ATOMS, GenericVoice, "child");
108 assert_peek_false!(CssAtomSet::ATOMS, GenericVoice, "auto");
109 }
110}