1use super::Location;
4use crate::error::{ParserError, PrinterError};
5use crate::parser::ParserOptions;
6use crate::printer::Printer;
7use crate::properties::font::FamilyName;
8use crate::traits::{Parse, ToCss};
9use crate::values::ident::Ident;
10use crate::values::number::CSSInteger;
11#[cfg(feature = "visitor")]
12use crate::visitor::Visit;
13use cssparser::*;
14use indexmap::IndexMap;
15use smallvec::SmallVec;
16use std::fmt::Write;
17
18#[derive(Debug, PartialEq, Clone)]
20#[cfg_attr(feature = "visitor", derive(Visit))]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
23#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
24pub struct FontFeatureValuesRule<'i> {
25 #[cfg_attr(feature = "serde", serde(borrow))]
27 pub name: Vec<FamilyName<'i>>,
28 pub rules: IndexMap<FontFeatureSubruleType, FontFeatureSubrule<'i>>,
30 #[cfg_attr(feature = "visitor", skip_visit)]
32 pub loc: Location,
33}
34
35impl<'i> FontFeatureValuesRule<'i> {
36 pub(crate) fn parse<'t>(
37 family_names: Vec<FamilyName<'i>>,
38 input: &mut Parser<'i, 't>,
39 loc: Location,
40 options: &ParserOptions<'i>,
41 ) -> Result<Self, ParseError<'i, ParserError<'i>>> {
42 let mut rules = IndexMap::new();
43 let mut rule_parser = FontFeatureValuesRuleParser {
44 rules: &mut rules,
45 options,
46 };
47 let mut parser = RuleBodyParser::new(input, &mut rule_parser);
48
49 while let Some(decl_or_rule) = parser.next() {
50 if let Err((err, _)) = decl_or_rule {
51 if parser.parser.options.error_recovery {
52 parser.parser.options.warn(err);
53 continue;
54 }
55 return Err(err);
56 }
57 }
58
59 Ok(FontFeatureValuesRule {
60 name: family_names,
61 rules,
62 loc,
63 })
64 }
65}
66
67struct FontFeatureValuesRuleParser<'a, 'i> {
68 rules: &'a mut IndexMap<FontFeatureSubruleType, FontFeatureSubrule<'i>>,
69 options: &'a ParserOptions<'i>,
70}
71
72impl<'a, 'i> cssparser::DeclarationParser<'i> for FontFeatureValuesRuleParser<'a, 'i> {
73 type Declaration = ();
74 type Error = ParserError<'i>;
75}
76
77impl<'a, 'i> cssparser::AtRuleParser<'i> for FontFeatureValuesRuleParser<'a, 'i> {
78 type Prelude = FontFeatureSubruleType;
79 type AtRule = ();
80 type Error = ParserError<'i>;
81
82 fn parse_prelude<'t>(
83 &mut self,
84 name: CowRcStr<'i>,
85 input: &mut Parser<'i, 't>,
86 ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
87 let loc = input.current_source_location();
88 FontFeatureSubruleType::parse_string(&name)
89 .map_err(|_| loc.new_custom_error(ParserError::AtRuleInvalid(name.clone().into())))
90 }
91
92 fn parse_block<'t>(
93 &mut self,
94 prelude: Self::Prelude,
95 start: &ParserState,
96 input: &mut Parser<'i, 't>,
97 ) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
98 let loc = start.source_location();
99 let mut decls = IndexMap::new();
100 let mut has_existing = false;
101 let declarations = if let Some(rule) = self.rules.get_mut(&prelude) {
102 has_existing = true;
103 &mut rule.declarations
104 } else {
105 &mut decls
106 };
107 let mut decl_parser = FontFeatureDeclarationParser { declarations };
108 let mut parser = RuleBodyParser::new(input, &mut decl_parser);
109 while let Some(decl) = parser.next() {
110 if let Err((err, _)) = decl {
111 if self.options.error_recovery {
112 self.options.warn(err);
113 continue;
114 }
115 return Err(err);
116 }
117 }
118
119 if !has_existing {
120 self.rules.insert(
121 prelude,
122 FontFeatureSubrule {
123 name: prelude,
124 declarations: decls,
125 loc: Location {
126 source_index: self.options.source_index,
127 line: loc.line,
128 column: loc.column,
129 },
130 },
131 );
132 }
133
134 Ok(())
135 }
136}
137
138impl<'a, 'i> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a, 'i> {
139 type Prelude = ();
140 type QualifiedRule = ();
141 type Error = ParserError<'i>;
142}
143
144impl<'a, 'i> RuleBodyItemParser<'i, (), ParserError<'i>> for FontFeatureValuesRuleParser<'a, 'i> {
145 fn parse_declarations(&self) -> bool {
146 false
147 }
148
149 fn parse_qualified(&self) -> bool {
150 false
151 }
152}
153
154impl<'i> ToCss for FontFeatureValuesRule<'i> {
155 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
156 where
157 W: std::fmt::Write,
158 {
159 #[cfg(feature = "sourcemap")]
160 dest.add_mapping(self.loc);
161 dest.write_str("@font-feature-values ")?;
162 self.name.to_css(dest)?;
163 dest.whitespace()?;
164 dest.write_char('{')?;
165 if !self.rules.is_empty() {
166 dest.newline()?;
167 for rule in self.rules.values() {
168 rule.to_css(dest)?;
169 dest.newline()?;
170 }
171 }
172 dest.write_char('}')
173 }
174}
175
176impl<'i> FontFeatureValuesRule<'i> {
177 pub(crate) fn merge(&mut self, other: &FontFeatureValuesRule<'i>) {
178 debug_assert_eq!(self.name, other.name);
179 for (prelude, rule) in &other.rules {
180 if let Some(existing) = self.rules.get_mut(prelude) {
181 existing
182 .declarations
183 .extend(rule.declarations.iter().map(|(k, v)| (k.clone(), v.clone())));
184 } else {
185 self.rules.insert(*prelude, rule.clone());
186 }
187 }
188 }
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Parse, ToCss)]
195#[cfg_attr(feature = "visitor", derive(Visit))]
196#[cfg_attr(
197 feature = "serde",
198 derive(serde::Serialize, serde::Deserialize),
199 serde(rename_all = "kebab-case")
200)]
201#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
202#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
203pub enum FontFeatureSubruleType {
204 Stylistic,
206 HistoricalForms,
208 Styleset,
210 CharacterVariant,
212 Swash,
214 Ornaments,
216 Annotation,
218}
219
220#[derive(Debug, PartialEq, Clone)]
223#[cfg_attr(feature = "visitor", derive(Visit))]
224#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
225#[cfg_attr(
226 feature = "serde",
227 derive(serde::Serialize, serde::Deserialize),
228 serde(rename_all = "camelCase")
229)]
230#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
231pub struct FontFeatureSubrule<'i> {
232 pub name: FontFeatureSubruleType,
234 #[cfg_attr(feature = "serde", serde(borrow))]
236 pub declarations: IndexMap<Ident<'i>, SmallVec<[CSSInteger; 1]>>,
237 #[cfg_attr(feature = "visitor", skip_visit)]
239 pub loc: Location,
240}
241
242impl<'i> ToCss for FontFeatureSubrule<'i> {
243 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
244 where
245 W: Write,
246 {
247 #[cfg(feature = "sourcemap")]
248 dest.add_mapping(self.loc);
249 dest.write_char('@')?;
250 self.name.to_css(dest)?;
251 dest.write_char('{')?;
252 dest.indent();
253 let len = self.declarations.len();
254 for (i, (name, value)) in self.declarations.iter().enumerate() {
255 dest.newline()?;
256 name.to_css(dest)?;
257 dest.delim(':', false)?;
258
259 let mut first = true;
260 for index in value {
261 if first {
262 first = false;
263 } else {
264 dest.write_char(' ')?;
265 }
266 index.to_css(dest)?;
267 }
268
269 if i != len - 1 || !dest.minify {
270 dest.write_char(';')?;
271 }
272 }
273 dest.dedent();
274 dest.newline()?;
275 dest.write_char('}')
276 }
277}
278
279struct FontFeatureDeclarationParser<'a, 'i> {
280 declarations: &'a mut IndexMap<Ident<'i>, SmallVec<[CSSInteger; 1]>>,
281}
282
283impl<'a, 'i> cssparser::DeclarationParser<'i> for FontFeatureDeclarationParser<'a, 'i> {
284 type Declaration = ();
285 type Error = ParserError<'i>;
286
287 fn parse_value<'t>(
288 &mut self,
289 name: CowRcStr<'i>,
290 input: &mut cssparser::Parser<'i, 't>,
291 _declaration_start: &ParserState,
292 ) -> Result<Self::Declaration, cssparser::ParseError<'i, Self::Error>> {
293 let mut indices = SmallVec::new();
294 loop {
295 if let Ok(value) = CSSInteger::parse(input) {
296 indices.push(value);
297 } else {
298 break;
299 }
300 }
301
302 if indices.is_empty() {
303 return Err(input.new_custom_error(ParserError::InvalidValue));
304 }
305
306 self.declarations.insert(Ident(name.into()), indices);
307 Ok(())
308 }
309}
310
311impl<'a, 'i> AtRuleParser<'i> for FontFeatureDeclarationParser<'a, 'i> {
313 type Prelude = ();
314 type AtRule = ();
315 type Error = ParserError<'i>;
316}
317
318impl<'a, 'i> QualifiedRuleParser<'i> for FontFeatureDeclarationParser<'a, 'i> {
319 type Prelude = ();
320 type QualifiedRule = ();
321 type Error = ParserError<'i>;
322}
323
324impl<'a, 'i> RuleBodyItemParser<'i, (), ParserError<'i>> for FontFeatureDeclarationParser<'a, 'i> {
325 fn parse_qualified(&self) -> bool {
326 false
327 }
328
329 fn parse_declarations(&self) -> bool {
330 true
331 }
332}