mod rule;
use nom::branch::alt;
use nom::bytes::complete::{is_not, tag};
use nom::combinator::recognize;
use nom::error::ParseError;
use nom::multi::many0;
use nom::IResult;
pub use self::rule::Rule;
use super::selector::{Selector, SelectorPath};
use super::token::{
comment0, parse_string_literal, parse_symbol, trim_whitespace, NeedsWhitespaceStringExt,
};
use crate::parser::ParseCss;
use crate::render::*;
use crate::transform::TransformCss;
#[derive(Debug, Clone)]
pub struct SelectorRuleset<'a, T>(pub Selector<'a>, pub Vec<T>);
impl<'a, T: RenderCss> RenderCss for SelectorRuleset<'a, T> {
fn render(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.render(f)?;
write!(f, "{{")?;
self.1.render(f)?;
write!(f, "}}")
}
}
impl<'a> TransformCss<SelectorPath<'a>> for SelectorRuleset<'a, Rule<'a>> {
fn transform_each<F: FnMut(&mut SelectorPath<'a>)>(&mut self, f: &mut F) {
self.0.transform_each(f)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct QualRule<'a>(pub &'a str, pub Option<&'a str>);
impl<'a> ParseCss<'a> for QualRule<'a> {
fn parse<E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, Self, E> {
let (input, _) = tag("@")(input)?;
let (input, tagname) = parse_symbol(input)?;
let (input, _) = comment0(input)?;
let (input, property) =
recognize(many0(alt((is_not("\";{}"), parse_string_literal()))))(input)?;
let property = if property.is_empty() {
None
} else {
Some(property)
};
Ok((input, QualRule(tagname, property)))
}
}
impl<'a> RenderCss for QualRule<'a> {
fn render(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "@")?;
trim_whitespace(self.0, f);
if let Some(val) = self.1 {
if val.needs_pre_ws() {
write!(f, " ")?;
}
trim_whitespace(val, f);
}
write!(f, ";")
}
}
#[derive(Debug, Clone)]
pub struct QualRuleset<'a, T>(pub QualRule<'a>, pub Vec<T>);
impl<'a, T: RenderCss> RenderCss for QualRuleset<'a, T> {
fn render(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if !self.1.is_empty() {
write!(f, "@{}", self.0 .0)?;
if let Some(val) = self.0 .1 {
if val.needs_pre_ws() {
write!(f, " ")?;
}
trim_whitespace(val, f);
}
write!(f, "{{")?;
self.1.render(f)?;
write!(f, "}}")
} else {
Ok(())
}
}
}
impl<'a, T: TransformCss<U>, U> TransformCss<U> for QualRuleset<'a, T> {
fn transform_each<F: FnMut(&mut U)>(&mut self, f: &mut F) {
for ruleset in self.1.iter_mut() {
ruleset.transform_each(f);
}
}
}
#[derive(Debug, Clone)]
pub struct QualNestedRuleset<'a, T>(pub QualRule<'a>, pub Vec<Ruleset<'a, T>>);
impl<'a, T: RenderCss> RenderCss for QualNestedRuleset<'a, T> {
fn render(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if !self.1.is_empty() {
let QualNestedRuleset(QualRule(name, val), node) = self;
write!(f, "@{}", name)?;
if let Some(val) = val {
if val.needs_pre_ws() {
write!(f, " ")?;
}
trim_whitespace(val, f);
}
write!(f, "{{")?;
node.render(f)?;
write!(f, "}}")
} else {
Ok(())
}
}
}
impl<'a, T, U> TransformCss<U> for QualNestedRuleset<'a, T>
where
Ruleset<'a, T>: TransformCss<U>,
{
fn transform_each<F: FnMut(&mut U)>(&mut self, f: &mut F) {
for ruleset in self.1.iter_mut() {
ruleset.transform_each(f);
}
}
}
#[allow(clippy::enum_variant_names)]
#[derive(Clone, Debug)]
pub enum Ruleset<'a, T> {
SelectorRuleset(SelectorRuleset<'a, T>),
QualRule(QualRule<'a>),
QualRuleset(QualRuleset<'a, T>),
QualNestedRuleset(QualNestedRuleset<'a, T>),
}
impl<'a, T: RenderCss> RenderCss for Ruleset<'a, T> {
fn render(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Ruleset::SelectorRuleset(selector_ruleset) => selector_ruleset.render(f),
Ruleset::QualRule(rule) => rule.render(f),
Ruleset::QualRuleset(ruleset) => ruleset.render(f),
Ruleset::QualNestedRuleset(ruleset) => ruleset.render(f),
}
}
}
impl<'a, T: TransformCss<Rule<'a>>> TransformCss<Rule<'a>> for Ruleset<'a, T> {
fn transform_each<F: FnMut(&mut Rule<'a>)>(&mut self, f: &mut F) {
match self {
Ruleset::QualRule(_) => (),
Ruleset::QualRuleset(rules) => rules.transform_each(f),
Ruleset::QualNestedRuleset(ruleset) => ruleset.transform_each(f),
Ruleset::SelectorRuleset(SelectorRuleset(_, rules)) => {
for rule in rules.iter_mut() {
rule.transform_each(f);
}
}
}
}
}