1#![deny(warnings)]
2use heck::ToPascalCase;
3use proc_macro2::{Span, TokenStream};
4use quote::{ToTokens, TokenStreamExt, format_ident};
5use std::{
6 fmt::Display,
7 ops::{Deref, Range},
8};
9use syn::{
10 Error, Ident, Lit, LitFloat, LitInt, LitStr, Result, Token, braced, bracketed,
11 ext::IdentExt,
12 parenthesized,
13 parse::{Parse, ParseStream},
14 parse2, token,
15};
16
17#[cfg(test)]
18mod test;
19
20pub struct StrWrapped<T: Parse>(pub T);
21impl<T: Parse> Parse for StrWrapped<T> {
22 fn parse(input_raw: ParseStream) -> Result<Self> {
23 Ok(Self(parse2::<T>(
24 input_raw.parse::<LitStr>()?.value().replace("'", "\"").replace("∞", "").parse::<TokenStream>()?,
25 )?))
26 }
27}
28
29#[derive(Debug, PartialEq, Clone)]
30pub enum Def {
31 Ident(DefIdent),
32 Function(DefIdent, Box<Def>),
33 AutoOr(Box<Def>),
34 NoneOr(Box<Def>),
35 AutoNoneOr(Box<Def>),
36 Type(DefType),
37 StyleValue(DefType),
38 FunctionType(DefType),
39 Optional(Box<Def>), Combinator(Vec<Def>, DefCombinatorStyle),
41 Group(Box<Def>, DefGroupStyle),
42 Multiplier(Box<Def>, DefMultiplierSeparator, DefRange),
43 Punct(char),
44 IntLiteral(i32),
45 DimensionLiteral(f32, String),
46}
47
48#[derive(Debug, PartialEq, Copy, Clone)]
49pub enum DefGroupStyle {
50 None, OneMustOccur, }
53
54#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
55pub enum DefCombinatorStyle {
56 Ordered, AllMustOccur, Options, Alternatives, }
61
62#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
63pub enum DefMultiplierSeparator {
64 None, Commas, }
67
68#[derive(Debug, PartialEq, Clone)]
69pub enum DefRange {
70 None,
71 Range(Range<f32>), RangeFrom(f32), RangeTo(f32), Fixed(f32), }
76
77#[derive(Debug, PartialEq, Clone)]
78pub struct DefIdent(pub String);
79
80#[derive(Debug, PartialEq, Clone)]
81pub struct DefType {
82 pub ident: DefIdent,
83 pub range: DefRange,
84}
85
86impl DefType {
87 pub fn new(str: &str, range: DefRange) -> Self {
88 DefType { ident: DefIdent(str.to_string()), range }
89 }
90
91 pub fn ident_str(&self) -> &str {
92 self.ident.0.as_str()
93 }
94
95 pub fn maybe_unsized(&self) -> bool {
96 matches!(
98 self.ident_str(),
99 "Image" | "Image1d" | "ContentList" | "CounterStyle" | "CursorImage" | "EasingFunction" | "LineWidthOrRepeat" | "LineWidthList" | "AutoLineWidthList" | "FamilyName" | "BgImage" | "DynamicRangeLimit" | "DynamicRangeLimitMixFunction" | "Outline"
116 | "SingleTransition"
117 | "Symbol" | "TransformList"
119 )
120 }
121}
122
123impl Parse for Def {
124 fn parse(input: ParseStream) -> Result<Self> {
125 let mut root = if input.peek(Token![<]) {
126 input.parse::<Token![<]>()?;
127 let mut style_value = false;
128 let mut function = false;
129 let ident = if input.peek(LitStr) {
130 style_value = true;
131 input.parse::<StrWrapped<DefIdent>>()?.0.0
132 } else {
133 input.parse::<DefIdent>()?.0
134 }
135 .to_pascal_case();
136 let range = if input.peek(token::Bracket) {
137 let content;
138 bracketed!(content in input);
139 content.parse::<DefRange>()?
140 } else {
141 DefRange::None
142 };
143 if input.peek(token::Paren) {
144 let content;
145 parenthesized!(content in input);
146 if !content.is_empty() {
147 Err(Error::new(input.span(), "disallowed content inside deftype function"))?
148 }
149 debug_assert!(!style_value, "Can't be function and style value");
150 function = true;
151 }
152 debug_assert!(!(function && style_value), "Can't be function or style value and or-none");
153 let ty = if let Some(without_auto) = ident.strip_suffix("-or-auto") {
154 Self::AutoOr(Box::new(Def::Type(DefType { ident: DefIdent(without_auto.into()), range })))
155 } else if let Some(without_none) = ident.strip_suffix("-or-none") {
156 Self::NoneOr(Box::new(Def::Type(DefType { ident: DefIdent(without_none.into()), range })))
157 } else if function {
158 Self::FunctionType(DefType { ident: DefIdent(ident), range })
159 } else if style_value {
160 Self::StyleValue(DefType { ident: DefIdent(ident), range })
161 } else {
162 Self::Type(DefType { ident: DefIdent(ident), range })
163 };
164 input.parse::<Token![>]>()?;
165 ty
166 } else if input.peek(token::Bracket) {
167 let content;
168 bracketed!(content in input);
169 let inner = Box::new(content.parse::<Def>()?);
170 if input.peek(Token![!]) {
171 input.parse::<Token![!]>()?;
172 Self::Group(inner, DefGroupStyle::OneMustOccur)
173 } else if input.peek(Token![#]) {
174 input.parse::<Token![#]>()?;
175 Self::Multiplier(inner, DefMultiplierSeparator::Commas, DefRange::RangeFrom(1.))
176 } else if input.peek(Token![+]) {
177 input.parse::<Token![+]>()?;
178 Self::Multiplier(inner, DefMultiplierSeparator::None, DefRange::RangeFrom(1.))
179 } else if input.peek(token::Brace) {
180 let content;
181 braced!(content in input);
182 let range = content.parse::<DefRange>()?;
183 debug_assert!(matches!(range, DefRange::Range(_) | DefRange::Fixed(_)));
184 Self::Multiplier(inner, DefMultiplierSeparator::None, range)
185 } else {
186 Self::Group(inner, DefGroupStyle::None)
187 }
188 } else if input.peek(Ident::peek_any) {
189 let ident = input.parse::<DefIdent>()?;
190 if input.peek(token::Paren) {
191 let content;
192 parenthesized!(content in input);
193 Self::Function(ident, Box::new(content.parse::<Def>()?))
194 } else {
195 Self::Ident(ident)
196 }
197 } else if input.peek(Lit) {
198 let lit = input.parse::<Lit>()?;
199 match lit {
200 Lit::Int(lit) => {
201 if lit.suffix() == "" {
202 Self::IntLiteral(lit.base10_parse::<i32>()?)
203 } else {
204 let unit = lit.suffix();
205 if unit.is_empty() {
206 Err(Error::new(lit.span(), "Invalid dimension unit"))?
207 }
208 Self::DimensionLiteral(lit.base10_parse::<f32>()?, unit.to_string())
209 }
210 }
211 Lit::Char(lit) => Self::Punct(lit.value()),
212 Lit::Str(lit) if lit.value().len() == 1 => Self::Punct(lit.value().chars().next().unwrap()),
213 _ => Err(Error::new(input.span(), "unknown token in Def parse"))?,
214 }
215 } else {
216 input.step(|cursor| {
217 if let Some((p, next)) = cursor.punct() {
218 return Ok((Self::Punct(p.as_char()), next));
219 }
220 Err(Error::new(input.span(), "unknown token in Def parse"))?
221 })?
222 }
223 .optimize();
224 loop {
225 if input.is_empty() {
226 return Ok(root);
227 } else if input.peek(Token![?]) {
228 input.parse::<Token![?]>()?;
229 let inner = root;
230 root = Self::Optional(Box::new(inner.optimize()));
231 } else if input.peek(Token![+])
232 || input.peek(Token![#])
233 || input.peek(token::Brace)
234 || input.peek(Token![*])
235 {
236 let inner = root;
237 let (sep, range) = if input.peek(Token![*]) {
238 input.parse::<Token![*]>()?;
239 (DefMultiplierSeparator::None, DefRange::RangeFrom(0.))
240 } else if input.peek(Token![+]) {
241 input.parse::<Token![+]>()?;
242 (DefMultiplierSeparator::None, DefRange::RangeFrom(1.))
243 } else if input.peek(Token![#]) {
244 input.parse::<Token![#]>()?;
245 let range = if input.peek(token::Brace) {
246 let content;
247 braced!(content in input);
248 content.parse::<DefRange>()?
249 } else if input.peek(Token![?]) {
250 input.parse::<Token![?]>()?;
251 DefRange::RangeFrom(0.)
252 } else {
253 DefRange::RangeFrom(1.)
254 };
255 (DefMultiplierSeparator::Commas, range)
256 } else if input.peek(token::Brace) {
257 let content;
258 braced!(content in input);
259 (DefMultiplierSeparator::None, content.parse::<DefRange>()?)
260 } else {
261 Err(Error::new(input.span(), "Unknown token in DefMultiplierStyle parse!"))?
262 };
263 root = Self::Multiplier(Box::new(inner.optimize()), sep, range).optimize();
264 } else {
265 let style = if input.peek(Token![||]) {
266 input.parse::<Token![||]>()?;
267 DefCombinatorStyle::Options
268 } else if input.peek(Token![|]) {
269 input.parse::<Token![|]>()?;
270 DefCombinatorStyle::Alternatives
271 } else if input.peek(Token![&&]) {
272 input.parse::<Token![&&]>()?;
273 DefCombinatorStyle::AllMustOccur
274 } else {
275 DefCombinatorStyle::Ordered
276 };
277 let mut next = input.parse::<Def>()?;
278 match (&mut root, &mut next) {
279 (_, &mut Self::Combinator(ref mut children, ref s)) if s == &style => {
280 children.insert(0, root);
281 root = next;
282 }
283 (&mut Self::Combinator(ref mut children, ref s), _) if s == &style => {
284 children.push(next);
285 }
286 (_, &mut Self::Combinator(ref mut children, ref other_style)) if &style < other_style => {
287 let options = Self::Combinator(vec![root, children.remove(0)], style);
288 children.insert(0, options);
289 root = next;
290 }
291 (_, Self::Group(inner, DefGroupStyle::None)) => {
292 let children = vec![root, inner.as_ref().clone()];
293 root = Self::Combinator(children, style);
294 }
295 (Self::Group(inner, DefGroupStyle::None), _) => {
296 let children = vec![inner.as_ref().clone(), next];
297 root = Self::Combinator(children, style);
298 }
299 _ => {
300 let children = vec![root, next];
301 root = Self::Combinator(children, style);
302 }
303 }
304 }
305 }
306 }
307}
308
309impl Def {
310 pub fn maybe_unsized(&self) -> bool {
313 match self {
314 Self::Ident(_) | Self::IntLiteral(_) | Self::DimensionLiteral(_, _) | Self::Punct(_) => false,
315 Self::Function(_, inner) => inner.maybe_unsized(),
317 Self::FunctionType(ty) => {
318 matches!(ty.ident_str(), "DynamicRangeLimitMix" | "Param" | "Repeat")
319 }
320 Self::Type(d) => d.maybe_unsized(),
321 Self::StyleValue(ty) => {
322 matches!(
323 ty.ident_str(),
324 "BorderTopColor"
325 | "ColumnRuleWidth"
326 | "DynamicRangeLimit"
327 | "EventTriggerName"
328 | "EventTriggerSource"
329 | "OutlineColor" | "PointerTimelineAxis"
330 | "PointerTimelineName"
331 | "AnimationRangeStart"
332 | "AnimationRangeEnd"
333 | "ScrollTimelineAxis"
334 | "ScrollTimelineName"
335 | "ViewTimelineAxis"
336 | "ViewTimelineName"
337 | "BorderTopClip"
338 )
339 }
340 Self::AutoOr(d) | Self::NoneOr(d) | Self::AutoNoneOr(d) => d.maybe_unsized(),
341 Self::Optional(d) => d.maybe_unsized(),
342 Self::Combinator(ds, _) => ds.iter().any(|d| d.maybe_unsized()),
343 Self::Group(d, _) => d.maybe_unsized(),
344 Self::Multiplier(_, _, _) => true,
345 }
346 }
347
348 pub fn suggested_data_type(&self) -> DataType {
349 match self {
350 Self::Combinator(_, DefCombinatorStyle::Alternatives) => DataType::Enum,
351 _ => DataType::SingleUnnamedStruct,
352 }
353 }
354
355 pub fn optimize(&self) -> Self {
356 match self {
357 Self::Combinator(defs, DefCombinatorStyle::Alternatives) if defs.len() == 2 => {
358 let [first, second] = defs.as_slice() else { panic!("defs.len() was 2!") };
359 match (first, second) {
360 (Def::Ident(DefIdent(ident)), Def::AutoOr(def))
362 | (Def::AutoOr(def), Def::Ident(DefIdent(ident)))
363 if ident == "none" =>
364 {
365 Def::AutoNoneOr(Box::new(*def.clone()))
366 }
367 (Def::Ident(DefIdent(ident)), Def::NoneOr(def))
369 | (Def::NoneOr(def), Def::Ident(DefIdent(ident)))
370 if ident == "auto" =>
371 {
372 Def::AutoNoneOr(Box::new(*def.clone()))
373 }
374 (Def::Ident(DefIdent(ident)), def) | (def, Def::Ident(DefIdent(ident)))
376 if ident == "auto" &&
377 !matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_)) =>
379 {
380 Def::AutoOr(Box::new(def.clone()))
381 }
382 (Def::Ident(DefIdent(ident)), def) | (def, Def::Ident(DefIdent(ident)))
384 if ident == "none" &&
385 !matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_)) =>
387 {
388 Def::NoneOr(Box::new(def.clone()))
389 }
390 (Def::Type(type1), Def::Type(type2)) => match (type1.ident_str(), type2.ident_str()) {
392 ("LengthPercentage", "Flex") | ("Flex", "LengthPercentage") => {
393 Def::Type(DefType::new("LengthPercentageOrFlex", type1.range.clone()))
394 }
395 ("Number", "Percentage") | ("Percentage", "Number") => {
396 Def::Type(DefType::new("NumberPercentage", type1.range.clone()))
397 }
398 ("Number", "Length") | ("Length", "Number") => {
399 Def::Type(DefType::new("NumberLength", type1.range.clone()))
400 }
401 _ => {
402 return Self::Combinator(
403 vec![first.optimize(), second.optimize()],
404 DefCombinatorStyle::Alternatives,
405 );
406 }
407 },
408 _ => {
409 return Self::Combinator(
410 vec![first.optimize(), second.optimize()],
411 DefCombinatorStyle::Alternatives,
412 );
413 }
414 }
415 }
416 Self::Combinator(defs, DefCombinatorStyle::Alternatives) if defs.len() == 3 => {
417 let [first, second, third] = defs.as_slice() else { panic!("defs.len() was 3!") };
418 match (first, second, third) {
419 (def, Def::Ident(DefIdent(first)), Def::Ident(DefIdent(second)))
421 | (Def::Ident(DefIdent(first)), def, Def::Ident(DefIdent(second)))
422 | (Def::Ident(DefIdent(first)), Def::Ident(DefIdent(second)), def)
423 if matches!((first.as_str(), second.as_str()), ("auto", "none") | ("none", "auto")) &&
424 !matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_)) =>
426 {
427 Def::AutoNoneOr(Box::new(def.clone()))
428 }
429 (Def::Type(type1), Def::Type(type2), Def::Ident(DefIdent(ident)))
431 | (Def::Ident(DefIdent(ident)), Def::Type(type1), Def::Type(type2))
432 | (Def::Type(type1), Def::Ident(DefIdent(ident)), Def::Type(type2))
433 if ident == "auto" =>
434 {
435 match (type1.ident_str(), type2.ident_str()) {
436 ("Number", "Length") | ("Length", "Number") => {
437 Def::AutoOr(Box::new(Def::Type(DefType::new("NumberLength", type1.range.clone()))))
438 }
439 ("Percentage", "Length") | ("Length", "Percentage") => {
440 Def::AutoOr(Box::new(Def::Type(DefType::new("LengthPercentage", type1.range.clone()))))
441 }
442 _ => {
443 return Self::Combinator(
444 vec![first.optimize(), second.optimize(), third.optimize()],
445 DefCombinatorStyle::Alternatives,
446 );
447 }
448 }
449 }
450 (def, Def::Type(type1), Def::Type(type2))
453 | (Def::Type(type1), def, Def::Type(type2))
454 | (Def::Type(type1), Def::Type(type2), def) => match (type1.ident_str(), type2.ident_str()) {
455 ("LengthPercentage", "Flex") | ("Flex", "LengthPercentage") => Def::Combinator(
456 vec![
457 def.optimize(),
458 Def::Type(DefType::new("LengthPercentageOrFlex", type1.range.clone())),
459 ],
460 DefCombinatorStyle::Alternatives,
461 ),
462 ("Number", "Percentage") | ("Percentage", "Number") => Def::Combinator(
463 vec![def.optimize(), Def::Type(DefType::new("NumberPercentage", type1.range.clone()))],
464 DefCombinatorStyle::Alternatives,
465 ),
466 ("Number", "Length") | ("Length", "Number") => Def::Combinator(
467 vec![def.optimize(), Def::Type(DefType::new("NumberLength", type1.range.clone()))],
468 DefCombinatorStyle::Alternatives,
469 ),
470 _ => {
471 return Self::Combinator(
472 vec![first.optimize(), second.optimize(), third.optimize()],
473 DefCombinatorStyle::Alternatives,
474 );
475 }
476 },
477 _ => {
478 return Self::Combinator(
479 vec![first.optimize(), second.optimize(), third.optimize()],
480 DefCombinatorStyle::Alternatives,
481 );
482 }
483 }
484 }
485 Self::Combinator(defs, style) => {
486 return Self::Combinator(defs.iter().map(|d| d.optimize()).collect(), *style);
487 }
488 Self::Multiplier(inner, DefMultiplierSeparator::None, DefRange::Fixed(i)) => {
491 let opts: Vec<_> = (1..=*i as u32).map(|_| inner.deref().clone()).collect();
492 Self::Combinator(opts, DefCombinatorStyle::Ordered)
493 }
494 Self::Multiplier(inner, DefMultiplierSeparator::None, DefRange::Range(Range { start, end })) => {
497 let opts: Vec<Def> = (1..=*end as i32)
498 .map(|i| if i <= (*start as i32) { inner.deref().clone() } else { Self::Optional(inner.clone()) })
499 .collect();
500 Self::Combinator(opts, DefCombinatorStyle::Ordered)
501 }
502 Self::Multiplier(inner, sep, range) => {
503 return Self::Multiplier(Box::new(inner.optimize()), *sep, range.clone());
504 }
505 Self::Optional(inner) => return Self::Optional(Box::new(inner.optimize())),
506 _ => return self.clone(),
507 }
508 .optimize()
509 }
510}
511
512impl Parse for DefIdent {
513 fn parse(input: ParseStream) -> Result<Self> {
514 let mut str = "".to_owned();
515 let mut last_was_ident = false;
516 loop {
517 if input.peek(Token![>]) || input.peek(token::Bracket) {
518 return Ok(Self(str));
519 } else if input.peek(Ident::peek_any) && !last_was_ident {
520 last_was_ident = true;
521 let ident = input.call(Ident::parse_any)?;
522 str.push_str(&ident.to_string());
523 } else if input.peek(LitInt) && last_was_ident {
525 last_was_ident = true;
526 let int = input.parse::<LitInt>()?;
527 str.push_str(&int.to_string());
528 } else if input.peek(Token![-]) {
529 last_was_ident = false;
530 input.parse::<Token![-]>()?;
531 str.push('-');
532 } else {
533 return Ok(Self(str));
534 }
535 }
536 }
537}
538
539impl Parse for DefRange {
540 fn parse(input: ParseStream) -> Result<Self> {
541 let mut lhs = None;
542 let mut rhs = None;
543 if input.peek(LitFloat) {
544 lhs = Some(input.parse::<LitFloat>()?.base10_parse()?);
545 } else if input.peek(LitInt) {
546 lhs = Some(input.parse::<LitInt>()?.base10_parse::<f32>()?);
547 }
548 if input.peek(Token![,]) {
549 input.parse::<Token![,]>()?;
550 if input.peek(LitFloat) {
551 rhs = Some(input.parse::<LitFloat>()?.base10_parse()?);
552 } else if input.peek(LitInt) {
553 rhs = Some(input.parse::<LitInt>()?.base10_parse::<f32>()?);
554 }
555 } else if let Some(lhs) = lhs {
556 return Ok(Self::Fixed(lhs));
557 }
558 Ok(match (lhs, rhs) {
559 (Some(start), Some(end)) => Self::Range(Range { start, end }),
560 (None, Some(end)) => Self::RangeTo(end),
561 (Some(start), None) => Self::RangeFrom(start),
562 (None, None) => Self::None,
563 })
564 }
565}
566
567pub enum DataType {
568 SingleUnnamedStruct,
569 Enum,
570}
571
572impl DataType {
573 pub fn is_struct(&self) -> bool {
574 matches!(self, Self::SingleUnnamedStruct)
575 }
576
577 pub fn is_enum(&self) -> bool {
578 matches!(self, Self::Enum)
579 }
580}
581
582impl Display for DefIdent {
583 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
584 self.0.fmt(f)
585 }
586}
587
588impl ToTokens for DefIdent {
589 fn to_tokens(&self, tokens: &mut TokenStream) {
590 tokens.append(Ident::new(&self.to_string(), Span::call_site()));
591 }
592}
593
594impl From<DefIdent> for Ident {
595 fn from(value: DefIdent) -> Self {
596 format_ident!("{}", value.0)
597 }
598}
599
600impl From<Ident> for DefIdent {
601 fn from(value: Ident) -> Self {
602 Self(value.to_string())
603 }
604}