css_value_definition_parser/
lib.rs

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	NormalOr(Box<Def>),
37	Type(DefType),
38	StyleValue(DefType),
39	FunctionType(DefType),
40	Optional(Box<Def>), // ?
41	Combinator(Vec<Def>, DefCombinatorStyle),
42	Group(Box<Def>, DefGroupStyle),
43	Multiplier(Box<Def>, DefMultiplierSeparator, DefRange),
44	Punct(char),
45	IntLiteral(i32),
46	DimensionLiteral(f32, String),
47}
48
49#[derive(Debug, PartialEq, Copy, Clone)]
50pub enum DefGroupStyle {
51	None,         // [ ] - regular group notation
52	OneMustOccur, // [ ]! - at least one in the group must occur
53}
54
55#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
56pub enum DefCombinatorStyle {
57	Ordered,      // <space>
58	AllMustOccur, // && - all must occur
59	Options,      // || - one or more must occur
60	Alternatives, // | - exactly one must occur
61}
62
63#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
64pub enum DefMultiplierSeparator {
65	None,   // *, +, or {,}
66	Commas, // #, #? or #{,}
67}
68
69#[derive(Debug, PartialEq, Clone)]
70pub enum DefRange {
71	None,
72	Range(Range<f32>), // {A,B}
73	RangeFrom(f32),    // {A,}
74	RangeTo(f32),      // {,B}
75	Fixed(f32),        // {A}
76}
77
78#[derive(Debug, PartialEq, Clone)]
79pub struct DefIdent(pub String);
80
81#[derive(Debug, PartialEq, Clone)]
82pub struct DefType {
83	pub ident: DefIdent,
84	pub range: DefRange,
85}
86
87impl DefType {
88	pub fn new(str: &str, range: DefRange) -> Self {
89		DefType { ident: DefIdent(str.to_string()), range }
90	}
91
92	pub fn ident_str(&self) -> &str {
93		self.ident.0.as_str()
94	}
95
96	pub fn maybe_unsized(&self) -> bool {
97		// Check for specific types that require allocations
98		matches!(
99			self.ident_str(),
100			// Hand-written types that contain other allocating types
101			"Color"          // Color<'a>
102				| "Shadow"       // Shadow<'a> (contains Color<'a>)
103				| "Image"          // contains Gradient<'a>
104				| "Image1d"  // contains StripesFunction<'a>
105				| "ContentList"  // Vec<'a, ContentListItem<'a>>
106				| "CounterStyle"  // complex hand-written type
107				| "CursorImage"  // contains Image<'a>
108				| "EasingFunction"  // contains LinearFunction<'a> with CommaSeparated
109				// Types that reference other allocating types
110					| "LineWidthOrRepeat"  // contains Repeat<'a>
111					| "LineWidthList"  // contains LineWidthOrRepeat<'a>
112					| "AutoLineWidthList"  // contains Repeat<'a> and LineWidthOrRepeat<'a>
113					| "GapRuleList"  // contains Vec<'a, ...>
114					| "GapAutoRuleList"  // contains Vec<'a, ...>
115					| "FamilyName"  // may contain allocating elements
116				| "BgImage"  // contains Image<'a>
117				| "DynamicRangeLimit"  // contains DynamicRangeLimitMixFunction<'a>
118				| "DynamicRangeLimitMixFunction"  // contains allocating params
119				// Additional types that reference allocating types
120				| "Outline"
121				| "SingleTransition"
122				| "Symbol" // Symbol<'a>
123				| "TransformList"
124		)
125	}
126}
127
128impl Parse for Def {
129	fn parse(input: ParseStream) -> Result<Self> {
130		let mut root = if input.peek(Token![<]) {
131			input.parse::<Token![<]>()?;
132			let mut style_value = false;
133			let mut function = false;
134			let ident = if input.peek(LitStr) {
135				style_value = true;
136				input.parse::<StrWrapped<DefIdent>>()?.0.0
137			} else {
138				input.parse::<DefIdent>()?.0
139			}
140			.to_pascal_case();
141			let range = if input.peek(token::Bracket) {
142				let content;
143				bracketed!(content in input);
144				content.parse::<DefRange>()?
145			} else {
146				DefRange::None
147			};
148			if input.peek(token::Paren) {
149				let content;
150				parenthesized!(content in input);
151				if !content.is_empty() {
152					Err(Error::new(input.span(), "disallowed content inside deftype function"))?
153				}
154				debug_assert!(!style_value, "Can't be function and style value");
155				function = true;
156			}
157			debug_assert!(!(function && style_value), "Can't be function or style value and or-none");
158			let ty = if let Some(without_auto) = ident.strip_suffix("-or-auto") {
159				Self::AutoOr(Box::new(Def::Type(DefType { ident: DefIdent(without_auto.into()), range })))
160			} else if let Some(without_none) = ident.strip_suffix("-or-none") {
161				Self::NoneOr(Box::new(Def::Type(DefType { ident: DefIdent(without_none.into()), range })))
162			} else if function {
163				Self::FunctionType(DefType { ident: DefIdent(ident), range })
164			} else if style_value {
165				Self::StyleValue(DefType { ident: DefIdent(ident), range })
166			} else {
167				Self::Type(DefType { ident: DefIdent(ident), range })
168			};
169			input.parse::<Token![>]>()?;
170			ty
171		} else if input.peek(token::Bracket) {
172			let content;
173			bracketed!(content in input);
174			let inner = Box::new(content.parse::<Def>()?);
175			if input.peek(Token![!]) {
176				input.parse::<Token![!]>()?;
177				Self::Group(inner, DefGroupStyle::OneMustOccur)
178			} else if input.peek(Token![#]) {
179				input.parse::<Token![#]>()?;
180				Self::Multiplier(inner, DefMultiplierSeparator::Commas, DefRange::RangeFrom(1.))
181			} else if input.peek(Token![+]) {
182				input.parse::<Token![+]>()?;
183				Self::Multiplier(inner, DefMultiplierSeparator::None, DefRange::RangeFrom(1.))
184			} else if input.peek(token::Brace) {
185				let content;
186				braced!(content in input);
187				let range = content.parse::<DefRange>()?;
188				debug_assert!(matches!(range, DefRange::Range(_) | DefRange::Fixed(_)));
189				Self::Multiplier(inner, DefMultiplierSeparator::None, range)
190			} else {
191				Self::Group(inner, DefGroupStyle::None)
192			}
193		} else if input.peek(Ident::peek_any) {
194			let ident = input.parse::<DefIdent>()?;
195			if input.peek(token::Paren) {
196				let content;
197				parenthesized!(content in input);
198				Self::Function(ident, Box::new(content.parse::<Def>()?))
199			} else {
200				Self::Ident(ident)
201			}
202		} else if input.peek(Lit) {
203			let lit = input.parse::<Lit>()?;
204			match lit {
205				Lit::Int(lit) => {
206					if lit.suffix() == "" {
207						Self::IntLiteral(lit.base10_parse::<i32>()?)
208					} else {
209						let unit = lit.suffix();
210						if unit.is_empty() {
211							Err(Error::new(lit.span(), "Invalid dimension unit"))?
212						}
213						Self::DimensionLiteral(lit.base10_parse::<f32>()?, unit.to_string())
214					}
215				}
216				Lit::Char(lit) => Self::Punct(lit.value()),
217				Lit::Str(lit) if lit.value().len() == 1 => Self::Punct(lit.value().chars().next().unwrap()),
218				_ => Err(Error::new(input.span(), "unknown token in Def parse"))?,
219			}
220		} else {
221			input.step(|cursor| {
222				if let Some((p, next)) = cursor.punct() {
223					return Ok((Self::Punct(p.as_char()), next));
224				}
225				Err(Error::new(input.span(), "unknown token in Def parse"))?
226			})?
227		}
228		.optimize();
229		loop {
230			if input.is_empty() {
231				return Ok(root);
232			} else if input.peek(Token![?]) {
233				input.parse::<Token![?]>()?;
234				let inner = root;
235				root = Self::Optional(Box::new(inner.optimize()));
236			} else if input.peek(Token![+])
237				|| input.peek(Token![#])
238				|| input.peek(token::Brace)
239				|| input.peek(Token![*])
240			{
241				let inner = root;
242				let (sep, range) = if input.peek(Token![*]) {
243					input.parse::<Token![*]>()?;
244					(DefMultiplierSeparator::None, DefRange::RangeFrom(0.))
245				} else if input.peek(Token![+]) {
246					input.parse::<Token![+]>()?;
247					(DefMultiplierSeparator::None, DefRange::RangeFrom(1.))
248				} else if input.peek(Token![#]) {
249					input.parse::<Token![#]>()?;
250					let range = if input.peek(token::Brace) {
251						let content;
252						braced!(content in input);
253						content.parse::<DefRange>()?
254					} else if input.peek(Token![?]) {
255						input.parse::<Token![?]>()?;
256						DefRange::RangeFrom(0.)
257					} else {
258						DefRange::RangeFrom(1.)
259					};
260					(DefMultiplierSeparator::Commas, range)
261				} else if input.peek(token::Brace) {
262					let content;
263					braced!(content in input);
264					(DefMultiplierSeparator::None, content.parse::<DefRange>()?)
265				} else {
266					Err(Error::new(input.span(), "Unknown token in DefMultiplierStyle parse!"))?
267				};
268				root = Self::Multiplier(Box::new(inner.optimize()), sep, range).optimize();
269			} else {
270				let style = if input.peek(Token![||]) {
271					input.parse::<Token![||]>()?;
272					DefCombinatorStyle::Options
273				} else if input.peek(Token![|]) {
274					input.parse::<Token![|]>()?;
275					DefCombinatorStyle::Alternatives
276				} else if input.peek(Token![&&]) {
277					input.parse::<Token![&&]>()?;
278					DefCombinatorStyle::AllMustOccur
279				} else {
280					DefCombinatorStyle::Ordered
281				};
282				let mut next = input.parse::<Def>()?;
283				match (&mut root, &mut next) {
284					(_, &mut Self::Combinator(ref mut children, ref s)) if s == &style => {
285						children.insert(0, root);
286						root = next;
287					}
288					(&mut Self::Combinator(ref mut children, ref s), _) if s == &style => {
289						children.push(next);
290					}
291					(_, &mut Self::Combinator(ref mut children, ref other_style)) if &style < other_style => {
292						let options = Self::Combinator(vec![root, children.remove(0)], style);
293						children.insert(0, options);
294						root = next;
295					}
296					(_, Self::Group(inner, DefGroupStyle::None)) => {
297						let children = vec![root, inner.as_ref().clone()];
298						root = Self::Combinator(children, style);
299					}
300					(Self::Group(inner, DefGroupStyle::None), _) => {
301						let children = vec![inner.as_ref().clone(), next];
302						root = Self::Combinator(children, style);
303					}
304					_ => {
305						let children = vec![root, next];
306						root = Self::Combinator(children, style);
307					}
308				}
309			}
310		}
311	}
312}
313
314impl Def {
315	/// Returns true if this type is unsized, in other words it requires heap allocations
316	/// to contain a full representation.
317	pub fn maybe_unsized(&self) -> bool {
318		match self {
319			Self::Ident(_) | Self::IntLiteral(_) | Self::DimensionLiteral(_, _) | Self::Punct(_) => false,
320			// Functions that contain multipliers or known allocating types
321			Self::Function(_, inner) => inner.maybe_unsized(),
322			Self::FunctionType(ty) => {
323				matches!(ty.ident_str(), "DynamicRangeLimitMix" | "Param" | "Repeat")
324			}
325			Self::Type(d) => d.maybe_unsized(),
326			Self::StyleValue(ty) => {
327				matches!(
328					ty.ident_str(),
329					"BorderBlockStart"
330						| "BorderTopColor" | "CaretColor"
331						| "ColumnRuleWidth"
332						| "DynamicRangeLimit"
333						| "EventTriggerName"
334						| "EventTriggerSource"
335						| "OutlineColor" | "PointerTimelineAxis"
336						| "PointerTimelineName"
337						| "AnimationRangeStart"
338						| "AnimationRangeEnd"
339						| "ScrollTimelineAxis"
340						| "ScrollTimelineName"
341						| "ViewTimelineAxis"
342						| "ViewTimelineName"
343						| "BorderTopClip" | "ColumnRule"
344						| "RowRule" | "TimelineTriggerActivationRange"
345						| "TimelineTriggerActivationRangeEnd"
346						| "TimelineTriggerActivationRangeStart"
347						| "TimelineTriggerActiveRange"
348						| "TimelineTriggerActiveRangeEnd"
349						| "TimelineTriggerActiveRangeStart"
350						| "TimelineTriggerName"
351						| "TimelineTriggerSource"
352				)
353			}
354			Self::AutoOr(d) | Self::NoneOr(d) | Self::AutoNoneOr(d) | Self::NormalOr(d) => d.maybe_unsized(),
355			Self::Optional(d) => d.maybe_unsized(),
356			Self::Combinator(ds, _) => ds.iter().any(|d| d.maybe_unsized()),
357			Self::Group(d, _) => d.maybe_unsized(),
358			Self::Multiplier(_, _, _) => true,
359		}
360	}
361
362	pub fn suggested_data_type(&self) -> DataType {
363		match self {
364			Self::Combinator(_, DefCombinatorStyle::Alternatives) => DataType::Enum,
365			_ => DataType::SingleUnnamedStruct,
366		}
367	}
368
369	pub fn optimize(&self) -> Self {
370		match self {
371			Self::Combinator(defs, DefCombinatorStyle::Alternatives) if defs.len() == 2 => {
372				let [first, second] = defs.as_slice() else { panic!("defs.len() was 2!") };
373				match (first, second) {
374					// "none | AutoOr<X>" can become "AutoNoneOr<X>"
375					(Def::Ident(DefIdent(ident)), Def::AutoOr(def))
376					| (Def::AutoOr(def), Def::Ident(DefIdent(ident)))
377						if ident == "none" =>
378					{
379						Def::AutoNoneOr(Box::new(*def.clone()))
380					}
381					// "auto | NoneOr<X>" can become "AutoNoneOr<X>"
382					(Def::Ident(DefIdent(ident)), Def::NoneOr(def))
383					| (Def::NoneOr(def), Def::Ident(DefIdent(ident)))
384						if ident == "auto" =>
385					{
386						Def::AutoNoneOr(Box::new(*def.clone()))
387					}
388					// "<X> | auto" can be simplified to "AutoOr<X>"
389					(Def::Ident(DefIdent(ident)), def) | (def, Def::Ident(DefIdent(ident)))
390						if ident == "auto" &&
391						// Avoid AutoOr<Ident>, or AutoOr<NoneOr<>> though
392						!matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_)) =>
393					{
394						Def::AutoOr(Box::new(def.clone()))
395					}
396					// "<X> | none" can be simplified to "NoneOr<X>"
397					(Def::Ident(DefIdent(ident)), def) | (def, Def::Ident(DefIdent(ident)))
398						if ident == "none"  &&
399						// Avoid NoneOr<Ident>, or NoneOr<AutoOr<>> though
400						!matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_)) =>
401					{
402						Def::NoneOr(Box::new(def.clone()))
403					}
404					// "<X> | normal" can be simplified to "NormalOr<X>"
405					(Def::Ident(DefIdent(ident)), def) | (def, Def::Ident(DefIdent(ident)))
406						if ident == "normal" &&
407						// Avoid NormalOr<Ident>, or NormalOr<AutoOr<>> though
408						!matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_) | Def::NormalOr(_)) =>
409					{
410						Def::NormalOr(Box::new(def.clone()))
411					}
412					// "<length-percentage> | <flex>" can be simplified to "<length-percentage-or-flex>"
413					(Def::Type(type1), Def::Type(type2)) => match (type1.ident_str(), type2.ident_str()) {
414						// "<gap-rule-list> | <gap-auto-rule-list>" can be flattened to "<gap-rule-list>"
415						("GapRuleList", "GapAutoRuleList") => {
416							Def::Type(DefType::new("GapRuleList", type1.range.clone()))
417						}
418						("GapAutoRuleList", "GapRuleList") => {
419							Def::Type(DefType::new("GapRuleList", type2.range.clone()))
420						}
421						("LengthPercentage", "Flex") | ("Flex", "LengthPercentage") => {
422							Def::Type(DefType::new("LengthPercentageOrFlex", type1.range.clone()))
423						}
424						("Number", "Percentage") | ("Percentage", "Number") => {
425							Def::Type(DefType::new("NumberPercentage", type1.range.clone()))
426						}
427						("Number", "Length") | ("Length", "Number") => {
428							Def::Type(DefType::new("NumberLength", type1.range.clone()))
429						}
430						_ => {
431							return Self::Combinator(
432								vec![first.optimize(), second.optimize()],
433								DefCombinatorStyle::Alternatives,
434							);
435						}
436					},
437					_ => {
438						return Self::Combinator(
439							vec![first.optimize(), second.optimize()],
440							DefCombinatorStyle::Alternatives,
441						);
442					}
443				}
444			}
445			Self::Combinator(defs, DefCombinatorStyle::Alternatives) if defs.len() == 3 => {
446				let [first, second, third] = defs.as_slice() else { panic!("defs.len() was 3!") };
447				match (first, second, third) {
448					// "auto | none | <X>" can be simplified to "AutoNoneOr<X>"
449					(def, Def::Ident(DefIdent(first)), Def::Ident(DefIdent(second)))
450					| (Def::Ident(DefIdent(first)), def, Def::Ident(DefIdent(second)))
451					| (Def::Ident(DefIdent(first)), Def::Ident(DefIdent(second)), def)
452						if matches!((first.as_str(), second.as_str()), ("auto", "none") | ("none", "auto")) &&
453						// Avoid AutoNoneOr<Ident>, or AutoNoneOr<AutoOr<>> though
454						!matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_)) =>
455					{
456						Def::AutoNoneOr(Box::new(def.clone()))
457					}
458					// "<number> | <length> | auto" can be simplified to "AutoOr<NumberLength>"
459					(Def::Type(type1), Def::Type(type2), Def::Ident(DefIdent(ident)))
460					| (Def::Ident(DefIdent(ident)), Def::Type(type1), Def::Type(type2))
461					| (Def::Type(type1), Def::Ident(DefIdent(ident)), Def::Type(type2))
462						if ident == "auto" =>
463					{
464						match (type1.ident_str(), type2.ident_str()) {
465							("Number", "Length") | ("Length", "Number") => {
466								Def::AutoOr(Box::new(Def::Type(DefType::new("NumberLength", type1.range.clone()))))
467							}
468							("Percentage", "Length") | ("Length", "Percentage") => {
469								Def::AutoOr(Box::new(Def::Type(DefType::new("LengthPercentage", type1.range.clone()))))
470							}
471							_ => {
472								return Self::Combinator(
473									vec![first.optimize(), second.optimize(), third.optimize()],
474									DefCombinatorStyle::Alternatives,
475								);
476							}
477						}
478					}
479					// "<x> | <length-percentage> | <flex>" can be simplified to "<x> | <length-percentage-or-flex>"
480					// "<x> | <number> | <percentage>" can be simplified to "<number-or-percentage>"
481					(def, Def::Type(type1), Def::Type(type2))
482					| (Def::Type(type1), def, Def::Type(type2))
483					| (Def::Type(type1), Def::Type(type2), def) => match (type1.ident_str(), type2.ident_str()) {
484						("LengthPercentage", "Flex") | ("Flex", "LengthPercentage") => Def::Combinator(
485							vec![
486								def.optimize(),
487								Def::Type(DefType::new("LengthPercentageOrFlex", type1.range.clone())),
488							],
489							DefCombinatorStyle::Alternatives,
490						),
491						("Number", "Percentage") | ("Percentage", "Number") => Def::Combinator(
492							vec![def.optimize(), Def::Type(DefType::new("NumberPercentage", type1.range.clone()))],
493							DefCombinatorStyle::Alternatives,
494						),
495						("Number", "Length") | ("Length", "Number") => Def::Combinator(
496							vec![def.optimize(), Def::Type(DefType::new("NumberLength", type1.range.clone()))],
497							DefCombinatorStyle::Alternatives,
498						),
499						_ => {
500							return Self::Combinator(
501								vec![first.optimize(), second.optimize(), third.optimize()],
502								DefCombinatorStyle::Alternatives,
503							);
504						}
505					},
506					_ => {
507						return Self::Combinator(
508							vec![first.optimize(), second.optimize(), third.optimize()],
509							DefCombinatorStyle::Alternatives,
510						);
511					}
512				}
513			}
514			Self::Combinator(defs, style) => {
515				return Self::Combinator(defs.iter().map(|d| d.optimize()).collect(), *style);
516			}
517			// Optimize multiplier styles to avoid unnecessarily allocating.
518			// A Multiplier with a fixed range can be normalised to an Ordered combinator of the same value.
519			Self::Multiplier(inner, DefMultiplierSeparator::None, DefRange::Fixed(i)) => {
520				let opts: Vec<_> = (1..=*i as u32).map(|_| inner.deref().clone()).collect();
521				Self::Combinator(opts, DefCombinatorStyle::Ordered)
522			}
523			// Optimize multiplier styles to avoid unnecessarily allocating.
524			// A multiplier with a bounded range can be normalised to an Ordered combinator of some optionals.
525			Self::Multiplier(inner, DefMultiplierSeparator::None, DefRange::Range(Range { start, end })) => {
526				let opts: Vec<Def> = (1..=*end as i32)
527					.map(|i| if i <= (*start as i32) { inner.deref().clone() } else { Self::Optional(inner.clone()) })
528					.collect();
529				Self::Combinator(opts, DefCombinatorStyle::Ordered)
530			}
531			Self::Multiplier(inner, sep, range) => {
532				return Self::Multiplier(Box::new(inner.optimize()), *sep, range.clone());
533			}
534			Self::Optional(inner) => return Self::Optional(Box::new(inner.optimize())),
535			Self::Group(inner, style) => return Self::Group(Box::new(inner.optimize()), *style),
536			_ => return self.clone(),
537		}
538		.optimize()
539	}
540}
541
542impl Parse for DefIdent {
543	fn parse(input: ParseStream) -> Result<Self> {
544		let mut str = "".to_owned();
545		let mut last_was_ident = false;
546		loop {
547			if input.peek(Token![>]) || input.peek(token::Bracket) {
548				return Ok(Self(str));
549			} else if input.peek(Ident::peek_any) && !last_was_ident {
550				last_was_ident = true;
551				let ident = input.call(Ident::parse_any)?;
552				str.push_str(&ident.to_string());
553			// LitInt might pick up identifier parts like "3d"
554			} else if input.peek(LitInt) && last_was_ident {
555				last_was_ident = true;
556				let int = input.parse::<LitInt>()?;
557				str.push_str(&int.to_string());
558			} else if input.peek(Token![-]) {
559				last_was_ident = false;
560				input.parse::<Token![-]>()?;
561				str.push('-');
562			} else {
563				return Ok(Self(str));
564			}
565		}
566	}
567}
568
569impl Parse for DefRange {
570	fn parse(input: ParseStream) -> Result<Self> {
571		let mut lhs = None;
572		let mut rhs = None;
573		if input.peek(LitFloat) {
574			lhs = Some(input.parse::<LitFloat>()?.base10_parse()?);
575		} else if input.peek(LitInt) {
576			lhs = Some(input.parse::<LitInt>()?.base10_parse::<f32>()?);
577		}
578		if input.peek(Token![,]) {
579			input.parse::<Token![,]>()?;
580			if input.peek(LitFloat) {
581				rhs = Some(input.parse::<LitFloat>()?.base10_parse()?);
582			} else if input.peek(LitInt) {
583				rhs = Some(input.parse::<LitInt>()?.base10_parse::<f32>()?);
584			}
585		} else if let Some(lhs) = lhs {
586			return Ok(Self::Fixed(lhs));
587		}
588		Ok(match (lhs, rhs) {
589			(Some(start), Some(end)) => Self::Range(Range { start, end }),
590			(None, Some(end)) => Self::RangeTo(end),
591			(Some(start), None) => Self::RangeFrom(start),
592			(None, None) => Self::None,
593		})
594	}
595}
596
597pub enum DataType {
598	SingleUnnamedStruct,
599	Enum,
600}
601
602impl DataType {
603	pub fn is_struct(&self) -> bool {
604		matches!(self, Self::SingleUnnamedStruct)
605	}
606
607	pub fn is_enum(&self) -> bool {
608		matches!(self, Self::Enum)
609	}
610}
611
612impl Display for DefIdent {
613	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
614		self.0.fmt(f)
615	}
616}
617
618impl ToTokens for DefIdent {
619	fn to_tokens(&self, tokens: &mut TokenStream) {
620		tokens.append(Ident::new(&self.to_string(), Span::call_site()));
621	}
622}
623
624impl From<DefIdent> for Ident {
625	fn from(value: DefIdent) -> Self {
626		format_ident!("{}", value.0)
627	}
628}
629
630impl From<Ident> for DefIdent {
631	fn from(value: Ident) -> Self {
632		Self(value.to_string())
633	}
634}