Skip to main content

css_ast/
unresolved.rs

1use crate::CssTypes;
2use css_parse::{ComponentValues, Cursor, Parse, Parser, Result};
3use csskit_derives::*;
4use csskit_proc_macro::node;
5
6/// ComponentValues, but with additional type information.
7///
8/// Produced when a substitution function (e.g. `var()`, `env()`) appears at a position where:
9/// - The full grammar type is known but the literal could not be parsed (`Value<T>::Unresolved`).
10/// - The entire value is a single substitution (`StyleValue`-level escape).
11///
12/// `tokens` preserves the original source tokens verbatim.
13/// `expected` records the set of CSS value types the position accepts, enabling
14/// downstream inference and diagnostics.
15#[node]
16#[derive(Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
18#[derive(csskit_derives::NodeWithMetadata)]
19pub struct Unresolved<'a> {
20	pub tokens: ComponentValues<'a>,
21	#[peek(skip)]
22	#[semantic_eq(skip)]
23	#[metadata(skip)]
24	pub expected: CssTypes,
25}
26
27impl<'a> Parse<'a> for Unresolved<'a> {
28	fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
29	where
30		I: Iterator<Item = Cursor> + Clone,
31	{
32		let tokens = p.parse::<ComponentValues>()?;
33		Ok(Self { tokens, expected: CssTypes::ANY })
34	}
35}
36
37#[cfg(test)]
38mod tests {
39	use super::*;
40	use crate::CssAtomSet;
41	use css_parse::assert_parse;
42
43	#[test]
44	fn test_expected_types_any() {
45		assert_eq!(CssTypes::ANY.bits(), !0u32);
46	}
47
48	#[test]
49	fn test_expected_types_compose() {
50		let lp = CssTypes::Length | CssTypes::Percentage;
51		assert!(lp.contains(CssTypes::Length));
52		assert!(lp.contains(CssTypes::Percentage));
53		assert!(!lp.contains(CssTypes::Color));
54	}
55
56	#[test]
57	fn test_parse_roundtrip() {
58		assert_parse!(CssAtomSet::ATOMS, Unresolved, "var(--x)");
59		assert_parse!(CssAtomSet::ATOMS, Unresolved, "1px solid red");
60	}
61}