Skip to main content

css_ast/types/
generic_voice.rs

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