use super::Location;
use crate::declaration::{parse_declaration, DeclarationBlock};
use crate::error::{ParserError, PrinterError};
use crate::macros::enum_property;
use crate::printer::Printer;
use crate::stylesheet::ParserOptions;
use crate::traits::{Parse, ToCss};
use crate::values::string::CowArcStr;
#[cfg(feature = "visitor")]
use crate::visitor::Visit;
use cssparser::*;
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase")
)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub struct PageSelector<'i> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub name: Option<CowArcStr<'i>>,
pub pseudo_classes: Vec<PagePseudoClass>,
}
enum_property! {
pub enum PagePseudoClass {
Left,
Right,
First,
Last,
Blank,
}
}
impl<'i> Parse<'i> for PageSelector<'i> {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
let name = input.try_parse(|input| input.expect_ident().map(|x| x.into())).ok();
let mut pseudo_classes = vec![];
loop {
let state = input.state();
match input.next_including_whitespace() {
Ok(Token::Colon) => {
pseudo_classes.push(PagePseudoClass::parse(input)?);
}
_ => {
input.reset(&state);
break;
}
}
}
if name.is_none() && pseudo_classes.is_empty() {
return Err(input.new_custom_error(ParserError::InvalidPageSelector));
}
Ok(PageSelector { name, pseudo_classes })
}
}
enum_property! {
pub enum PageMarginBox {
TopLeftCorner,
TopLeft,
TopCenter,
TopRight,
TopRightCorner,
LeftTop,
LeftMiddle,
LeftBottom,
RightTop,
RightMiddle,
RightBottom,
BottomLeftCorner,
BottomLeft,
BottomCenter,
BottomRight,
BottomRightCorner,
}
}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase")
)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub struct PageMarginRule<'i> {
pub margin_box: PageMarginBox,
#[cfg_attr(feature = "serde", serde(borrow))]
pub declarations: DeclarationBlock<'i>,
#[cfg_attr(feature = "visitor", skip_visit)]
pub loc: Location,
}
impl<'i> ToCss for PageMarginRule<'i> {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
#[cfg(feature = "sourcemap")]
dest.add_mapping(self.loc);
dest.write_char('@')?;
self.margin_box.to_css(dest)?;
self.declarations.to_css_block(dest)
}
}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub struct PageRule<'i> {
#[cfg_attr(feature = "serde", serde(borrow))]
#[cfg_attr(feature = "visitor", skip_visit)]
pub selectors: Vec<PageSelector<'i>>,
pub declarations: DeclarationBlock<'i>,
pub rules: Vec<PageMarginRule<'i>>,
#[cfg_attr(feature = "visitor", skip_visit)]
pub loc: Location,
}
impl<'i> PageRule<'i> {
pub(crate) fn parse<'t, 'o>(
selectors: Vec<PageSelector<'i>>,
input: &mut Parser<'i, 't>,
loc: Location,
options: &ParserOptions<'o, 'i>,
) -> Result<Self, ParseError<'i, ParserError<'i>>> {
let mut declarations = DeclarationBlock::new();
let mut rules = Vec::new();
let mut rule_parser = PageRuleParser {
declarations: &mut declarations,
rules: &mut rules,
options: &options,
};
let mut parser = RuleBodyParser::new(input, &mut rule_parser);
while let Some(decl) = parser.next() {
if let Err((err, _)) = decl {
if parser.parser.options.error_recovery {
parser.parser.options.warn(err);
continue;
}
return Err(err);
}
}
Ok(PageRule {
selectors,
declarations,
rules,
loc,
})
}
}
impl<'i> ToCss for PageRule<'i> {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
#[cfg(feature = "sourcemap")]
dest.add_mapping(self.loc);
dest.write_str("@page")?;
if let Some(first) = self.selectors.first() {
if !dest.minify || first.name.is_some() {
dest.write_char(' ')?;
}
let mut first = true;
for selector in &self.selectors {
if first {
first = false;
} else {
dest.delim(',', false)?;
}
selector.to_css(dest)?;
}
}
dest.whitespace()?;
dest.write_char('{')?;
dest.indent();
let mut i = 0;
let len = self.declarations.len() + self.rules.len();
macro_rules! write {
($decls: expr, $important: literal) => {
for decl in &$decls {
dest.newline()?;
decl.to_css(dest, $important)?;
if i != len - 1 || !dest.minify {
dest.write_char(';')?;
}
i += 1;
}
};
}
write!(self.declarations.declarations, false);
write!(self.declarations.important_declarations, true);
if !self.rules.is_empty() {
if !dest.minify && self.declarations.len() > 0 {
dest.write_char('\n')?;
}
dest.newline()?;
let mut first = true;
for rule in &self.rules {
if first {
first = false;
} else {
if !dest.minify {
dest.write_char('\n')?;
}
dest.newline()?;
}
rule.to_css(dest)?;
}
}
dest.dedent();
dest.newline()?;
dest.write_char('}')
}
}
impl<'i> ToCss for PageSelector<'i> {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
if let Some(name) = &self.name {
dest.write_str(&name)?;
}
for pseudo in &self.pseudo_classes {
dest.write_char(':')?;
pseudo.to_css(dest)?;
}
Ok(())
}
}
struct PageRuleParser<'a, 'o, 'i> {
declarations: &'a mut DeclarationBlock<'i>,
rules: &'a mut Vec<PageMarginRule<'i>>,
options: &'a ParserOptions<'o, 'i>,
}
impl<'a, 'o, 'i> cssparser::DeclarationParser<'i> for PageRuleParser<'a, 'o, 'i> {
type Declaration = ();
type Error = ParserError<'i>;
fn parse_value<'t>(
&mut self,
name: CowRcStr<'i>,
input: &mut cssparser::Parser<'i, 't>,
) -> Result<Self::Declaration, cssparser::ParseError<'i, Self::Error>> {
parse_declaration(
name,
input,
&mut self.declarations.declarations,
&mut self.declarations.important_declarations,
&self.options,
)
}
}
impl<'a, 'o, 'i> AtRuleParser<'i> for PageRuleParser<'a, 'o, 'i> {
type Prelude = PageMarginBox;
type AtRule = ();
type Error = ParserError<'i>;
fn parse_prelude<'t>(
&mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
let loc = input.current_source_location();
PageMarginBox::parse_string(&name)
.map_err(|_| loc.new_custom_error(ParserError::AtRuleInvalid(name.clone().into())))
}
fn parse_block<'t>(
&mut self,
prelude: Self::Prelude,
start: &ParserState,
input: &mut Parser<'i, 't>,
) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
let loc = start.source_location();
let declarations = DeclarationBlock::parse(input, self.options)?;
self.rules.push(PageMarginRule {
margin_box: prelude,
declarations,
loc: Location {
source_index: self.options.source_index,
line: loc.line,
column: loc.column,
},
});
Ok(())
}
}
impl<'a, 'o, 'i> QualifiedRuleParser<'i> for PageRuleParser<'a, 'o, 'i> {
type Prelude = ();
type QualifiedRule = ();
type Error = ParserError<'i>;
}
impl<'a, 'o, 'i> RuleBodyItemParser<'i, (), ParserError<'i>> for PageRuleParser<'a, 'o, 'i> {
fn parse_qualified(&self) -> bool {
false
}
fn parse_declarations(&self) -> bool {
true
}
}