css_ast/types/
url.rs

1use css_parse::{Build, Cursor, Parse, Parser, Peek, Result as ParserResult, T, function_set};
2use csskit_derives::{ToCursors, ToSpan, Visitable};
3
4/// <https://drafts.csswg.org/css-values-4/#url-value>
5///
6/// ```text
7/// <url> = <url()> | <src()>
8/// <url()> = url( <string> <url-modifier>* ) | <url-token>
9/// <src()> = src( <string> <url-modifier>* )
10/// ```
11#[derive(ToCursors, ToSpan, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
13#[visit(self)]
14pub enum Url {
15	Url(T![Url]),
16	UrlFunction(T![Function], T![String], T![')']),
17	SrcFunction(T![Function], T![String], T![')']),
18}
19
20function_set!(
21	pub enum UrlFunctionKeywords {
22		Url: "url",
23		Src: "src"
24	}
25);
26
27impl<'a> Peek<'a> for Url {
28	fn peek(p: &Parser<'a>, c: Cursor) -> bool {
29		<T![Url]>::peek(p, c) || <UrlFunctionKeywords>::peek(p, c)
30	}
31}
32
33impl<'a> Parse<'a> for Url {
34	fn parse(p: &mut Parser<'a>) -> ParserResult<Self> {
35		if let Some(url) = p.parse_if_peek::<T![Url]>()? {
36			return Ok(Self::Url(url));
37		}
38		let keyword = p.parse::<UrlFunctionKeywords>()?;
39		let c: Cursor = keyword.into();
40		let function = <T![Function]>::build(p, c);
41		match keyword {
42			UrlFunctionKeywords::Url(_) => {
43				let string = p.parse::<T![String]>()?;
44				let close = p.parse::<T![')']>()?;
45				Ok(Self::SrcFunction(function, string, close))
46			}
47			UrlFunctionKeywords::Src(_) => {
48				let string = p.parse::<T![String]>()?;
49				let close = p.parse::<T![')']>()?;
50				Ok(Self::SrcFunction(function, string, close))
51			}
52		}
53	}
54}
55
56#[cfg(test)]
57mod tests {
58	use super::*;
59	use css_parse::assert_parse;
60
61	#[test]
62	fn size_test() {
63		assert_eq!(std::mem::size_of::<Url>(), 40);
64	}
65
66	#[test]
67	fn test_writes() {
68		assert_parse!(Url, "url('foo')");
69		assert_parse!(Url, "url(\"foo\")");
70		assert_parse!(Url, "url(foo)");
71	}
72}