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	Type(DefType),
37	StyleValue(DefType),
38	FunctionType(DefType),
39	Optional(Box<Def>), // ?
40	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,         // [ ] - regular group notation
51	OneMustOccur, // [ ]! - at least one in the group must occur
52}
53
54#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
55pub enum DefCombinatorStyle {
56	Ordered,      // <space>
57	AllMustOccur, // && - all must occur
58	Options,      // || - one or more must occur
59	Alternatives, // | - exactly one must occur
60}
61
62#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
63pub enum DefMultiplierSeparator {
64	None,   // *, +, or {,}
65	Commas, // #, #? or #{,}
66}
67
68#[derive(Debug, PartialEq, Clone)]
69pub enum DefRange {
70	None,
71	Range(Range<f32>), // {A,B}
72	RangeFrom(f32),    // {A,}
73	RangeTo(f32),      // {,B}
74	Fixed(f32),        // {A}
75}
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		// Check for specific types that require allocations
97		matches!(
98			self.ident_str(),
99			// Hand-written types that contain other allocating types
100			"Image"          // contains Gradient<'a>
101				| "Image1d"  // contains StripesFunction<'a>
102				| "ContentList"  // Vec<'a, ContentListItem<'a>>
103				| "CounterStyle"  // complex hand-written type
104				| "CursorImage"  // contains Image<'a>
105				| "EasingFunction"  // contains LinearFunction<'a> with CommaSeparated
106				// Types that reference other allocating types
107				| "LineWidthOrRepeat"  // contains Repeat<'a>
108				| "LineWidthList"  // contains LineWidthOrRepeat<'a>
109				| "AutoLineWidthList"  // contains Repeat<'a> and LineWidthOrRepeat<'a>
110				| "FamilyName"  // may contain allocating elements
111				| "BgImage"  // contains Image<'a>
112				| "DynamicRangeLimit"  // contains DynamicRangeLimitMixFunction<'a>
113				| "DynamicRangeLimitMixFunction"  // contains allocating params
114				// Additional types that reference allocating types
115				| "Outline"
116				| "SingleTransition"
117				| "Symbol" // Symbol<'a>
118				| "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	/// Returns true if this type is unsized, in other words it requires heap allocations
311	/// to contain a full representation.
312	pub fn maybe_unsized(&self) -> bool {
313		match self {
314			Self::Ident(_) | Self::IntLiteral(_) | Self::DimensionLiteral(_, _) | Self::Punct(_) => false,
315			// Functions that contain multipliers or known allocating types
316			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					// "none | AutoOr<X>" can become "AutoNoneOr<X>"
361					(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					// "auto | NoneOr<X>" can become "AutoNoneOr<X>"
368					(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					// "<X> | auto" can be simplified to "AutoOr<X>"
375					(Def::Ident(DefIdent(ident)), def) | (def, Def::Ident(DefIdent(ident)))
376						if ident == "auto" &&
377						// Avoid AutoOr<Ident>, or AutoOr<NoneOr<>> though
378						!matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_)) =>
379					{
380						Def::AutoOr(Box::new(def.clone()))
381					}
382					// "<X> | none" can be simplified to "NoneOr<X>"
383					(Def::Ident(DefIdent(ident)), def) | (def, Def::Ident(DefIdent(ident)))
384						if ident == "none"  &&
385						// Avoid NoneOr<Ident>, or NoneOr<AutoOr<>> though
386						!matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_)) =>
387					{
388						Def::NoneOr(Box::new(def.clone()))
389					}
390					// "<length-percentage> | <flex>" can be simplified to "<length-percentage-or-flex>"
391					(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					// "auto | none | <X>" can be simplified to "AutoNoneOr<X>"
420					(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						// Avoid AutoNoneOr<Ident>, or AutoNoneOr<AutoOr<>> though
425						!matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_)) =>
426					{
427						Def::AutoNoneOr(Box::new(def.clone()))
428					}
429					// "<number> | <length> | auto" can be simplified to "AutoOr<NumberLength>"
430					(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					// "<x> | <length-percentage> | <flex>" can be simplified to "<x> | <length-percentage-or-flex>"
451					// "<x> | <number> | <percentage>" can be simplified to "<number-or-percentage>"
452					(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			// Optimize multiplier styles to avoid unnecessarily allocating.
489			// A Multiplier with a fixed range can be normalised to an Ordered combinator of the same value.
490			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			// Optimize multiplier styles to avoid unnecessarily allocating.
495			// A multiplier with a bounded range can be normalised to an Ordered combinator of some optionals.
496			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			// LitInt might pick up identifier parts like "3d"
524			} 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}