procss/ast/ruleset/
rule.rs1use std::borrow::Cow;
13
14use nom::branch::alt;
15use nom::bytes::complete::{is_not, tag};
16use nom::combinator::recognize;
17use nom::error::ParseError;
18use nom::multi::many0;
19use nom::sequence::tuple;
20use nom::IResult;
21
22use crate::ast::token::{comment0, parse_string_literal, parse_symbol, trim_whitespace};
23use crate::render::RenderCss;
24use crate::transform::TransformCss;
25
26#[derive(Clone, Debug, Eq, PartialEq, Hash)]
28pub struct Rule<'a> {
29 pub property: Cow<'a, str>,
30 pub value: Cow<'a, str>,
31}
32
33impl<'a> TransformCss<Rule<'a>> for Rule<'a> {
34 fn transform_each<F: FnMut(&mut Rule<'a>)>(&mut self, f: &mut F) {
35 f(self)
36 }
37}
38
39impl<'a> RenderCss for Rule<'a> {
40 fn render(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 let Rule { property, value } = self;
42 write!(f, "{}:", property)?;
43 trim_whitespace(value, f);
44 write!(f, ";")
45 }
46}
47
48impl<'a> crate::parser::ParseCss<'a> for Rule<'a> {
51 fn parse<E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, Self, E> {
52 let (input, property) = parse_symbol(input)?;
53 let (input, _) = tuple((comment0, tag(":"), comment0))(input)?;
54 let (input, value) =
55 recognize(many0(alt((is_not("\";}"), parse_string_literal()))))(input)?;
56 Ok((input, Rule {
57 property: property.into(),
58 value: value.into(),
59 }))
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use std::assert_matches::assert_matches;
66
67 use super::*;
68 use crate::parser::ParseCss;
69
70 #[test]
71 fn test_rule_value_string() {
72 assert_matches!(
73 Rule::parse::<()>("--column-selector--background: url(\"test\")"),
74 Ok(("", Rule {
75 property,
76 value,
77 })) if value == "url(\"test\")" && property == "--column-selector--background"
78 )
79 }
80
81 #[test]
82 fn test_rule_escaped_string() {
83 assert_matches!(
84 Rule::parse::<()>("test: \"\\1234\""),
85 Ok(("", Rule {
86 property,
87 value,
88 })) if value == "\"\\1234\"" && property == "test"
89 )
90 }
91
92 #[test]
93 fn test_rule_escaped_string_2() {
94 assert_matches!(
95 Rule::parse::<()>("test: \": test ; alpha\""),
96 Ok(("", Rule {
97 property,
98 value,
99 })) if value == "\": test ; alpha\"" && property == "test"
100 )
101 }
102
103 #[ignore]
104 #[test]
105 fn test_rule_escaped_string_3() {
106 assert_matches!(
107 Rule::parse::<()>("test: ': test ; alpha'"),
108 Ok(("", Rule {
109 property,
110 value,
111 })) if value == "\"\\1234\"" && property == "test"
112 )
113 }
114}