#![allow(non_upper_case_globals)]
use bitflags::bitflags;
use cssparser::*;
use smallvec::SmallVec;
use crate::{
context::PropertyHandlerContext,
declaration::{DeclarationBlock, DeclarationList},
error::{ParserError, PrinterError},
macros::{define_shorthand, shorthand_handler},
printer::Printer,
properties::{Property, PropertyId},
rules::container::ContainerName as ContainerIdent,
targets::Browsers,
traits::{Parse, PropertyHandler, Shorthand, ToCss},
visitor::Visit,
};
bitflags! {
#[derive(Visit)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ContainerType: u8 {
const Normal = 0b00000001;
const InlineSize = 0b00000010;
const Size = 0b00000100;
}
}
impl Default for ContainerType {
fn default() -> Self {
ContainerType::Normal
}
}
impl<'i> Parse<'i> for ContainerType {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
let mut flags = ContainerType::empty();
while let Ok(ident) = input.try_parse(|input| input.expect_ident_cloned()) {
let location = input.current_source_location();
let flag = match_ignore_ascii_case! { &ident,
"normal" if flags.is_empty() => return Ok(ContainerType::Normal), "size" => ContainerType::Size,
"inline-size" => ContainerType::InlineSize,
_ => return Err(location.new_unexpected_token_error(
cssparser::Token::Ident(ident.clone())
))
};
if flags.contains(flag) {
return Err(location.new_unexpected_token_error(cssparser::Token::Ident(ident.clone())));
}
flags |= flag;
}
if flags.is_empty() {
return Err(input.new_error_for_next_token());
} else {
return Ok(flags);
}
}
}
impl ToCss for ContainerType {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
if self.contains(ContainerType::Normal) {
dest.write_str("normal")?;
} else if self.contains(ContainerType::Size) {
dest.write_str("size")?;
} else if self.contains(ContainerType::InlineSize) {
dest.write_str("inline-size")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Visit)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type", content = "value", rename_all = "kebab-case")
)]
pub enum ContainerNameList<'i> {
None,
#[cfg_attr(feature = "serde", serde(borrow))]
Names(SmallVec<[ContainerIdent<'i>; 1]>),
}
impl<'i> Default for ContainerNameList<'i> {
fn default() -> Self {
ContainerNameList::None
}
}
impl<'i> Parse<'i> for ContainerNameList<'i> {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(ContainerNameList::None);
}
let mut names = SmallVec::new();
while let Ok(name) = input.try_parse(ContainerIdent::parse) {
names.push(name);
}
if names.is_empty() {
return Err(input.new_error_for_next_token());
} else {
return Ok(ContainerNameList::Names(names));
}
}
}
impl<'i> ToCss for ContainerNameList<'i> {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
ContainerNameList::None => dest.write_str("none"),
ContainerNameList::Names(names) => {
let mut first = true;
for name in names {
if first {
first = false;
} else {
dest.write_char(' ')?;
}
name.to_css(dest)?;
}
Ok(())
}
}
}
}
define_shorthand! {
pub struct Container<'i> {
#[cfg_attr(feature = "serde", serde(borrow))]
name: ContainerName(ContainerNameList<'i>),
container_type: ContainerType(ContainerType),
}
}
impl<'i> Parse<'i> for Container<'i> {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
let name = ContainerNameList::parse(input)?;
let container_type = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
ContainerType::parse(input)?
} else {
ContainerType::default()
};
Ok(Container { name, container_type })
}
}
impl<'i> ToCss for Container<'i> {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
self.name.to_css(dest)?;
if self.container_type != ContainerType::default() {
dest.delim('/', true)?;
self.container_type.to_css(dest)?;
}
Ok(())
}
}
shorthand_handler!(ContainerHandler -> Container<'i> {
name: ContainerName(ContainerNameList<'i>),
container_type: ContainerType(ContainerType),
});