#![warn(unused_must_use)]
use crate as css;
use crate::{Parser, PrintErr, Printer, VendorPrefix};
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum Display {
Keyword(DisplayKeyword),
Pair(DisplayPair),
}
impl Display {
pub(crate) fn parse(input: &mut Parser) -> css::Result<Self> {
if let Ok(kw) = input.try_parse(DisplayKeyword::parse) {
return Ok(Display::Keyword(kw));
}
DisplayPair::parse(input).map(Display::Pair)
}
pub(crate) fn to_css(self, dest: &mut Printer) -> Result<(), PrintErr> {
match self {
Display::Keyword(kw) => kw.to_css(dest),
Display::Pair(p) => p.to_css(dest),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, css::DefineEnumProperty)]
pub enum Visibility {
Visible,
Hidden,
Collapse,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, css::DefineEnumProperty)]
pub enum DisplayKeyword {
None,
Contents,
TableRowGroup,
TableHeaderGroup,
TableFooterGroup,
TableRow,
TableCell,
TableColumnGroup,
TableColumn,
TableCaption,
RubyBase,
RubyText,
RubyBaseContainer,
RubyTextContainer,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct DisplayPair {
pub outside: DisplayOutside,
pub inside: DisplayInside,
pub is_list_item: bool,
}
impl DisplayPair {
pub(crate) fn parse(input: &mut Parser) -> css::Result<Self> {
let mut list_item = false;
let mut outside: Option<DisplayOutside> = None;
let mut inside: Option<DisplayInside> = None;
loop {
if input
.try_parse(|i| i.expect_ident_matching(b"list-item"))
.is_ok()
{
list_item = true;
continue;
}
if outside.is_none() {
if let Ok(o) = input.try_parse(DisplayOutside::parse) {
outside = Some(o);
continue;
}
}
if inside.is_none() {
if let Ok(i) = input.try_parse(DisplayInside::parse) {
inside = Some(i);
continue;
}
}
break;
}
if list_item || inside.is_some() || outside.is_some() {
let final_inside: DisplayInside = inside.unwrap_or(DisplayInside::Flow);
let final_outside: DisplayOutside = outside.unwrap_or(match final_inside {
DisplayInside::Ruby => DisplayOutside::Inline,
_ => DisplayOutside::Block,
});
if list_item && !matches!(final_inside, DisplayInside::Flow | DisplayInside::FlowRoot) {
return Err(input.new_custom_error(css::ParserError::invalid_declaration));
}
return Ok(DisplayPair {
outside: final_outside,
inside: final_inside,
is_list_item: list_item,
});
}
let location = input.current_source_location();
let ident = input.expect_ident_cloned()?;
use bun_core::eql_case_insensitive_ascii as eq;
let inside = if eq(ident, b"inline-block", true) {
DisplayInside::FlowRoot
} else if eq(ident, b"inline-table", true) {
DisplayInside::Table
} else if eq(ident, b"inline-flex", true) {
DisplayInside::Flex(VendorPrefix::NONE)
} else if eq(ident, b"-webkit-inline-flex", true) {
DisplayInside::Flex(VendorPrefix::WEBKIT)
} else if eq(ident, b"-ms-inline-flexbox", true) {
DisplayInside::Flex(VendorPrefix::MS)
} else if eq(ident, b"-webkit-inline-box", true) {
DisplayInside::Box(VendorPrefix::WEBKIT)
} else if eq(ident, b"-moz-inline-box", true) {
DisplayInside::Box(VendorPrefix::MOZ)
} else if eq(ident, b"inline-grid", true) {
DisplayInside::Grid
} else {
return Err(location.new_unexpected_token_error(css::Token::Ident(ident)));
};
Ok(DisplayPair {
outside: DisplayOutside::Inline,
inside,
is_list_item: false,
})
}
pub(crate) fn to_css(self, dest: &mut Printer) -> Result<(), PrintErr> {
match (self.outside, &self.inside, self.is_list_item) {
(DisplayOutside::Inline, DisplayInside::FlowRoot, false) => {
return dest.write_str("inline-block");
}
(DisplayOutside::Inline, DisplayInside::Table, false) => {
return dest.write_str("inline-table");
}
(DisplayOutside::Inline, DisplayInside::Flex(prefix), false) => {
prefix.to_css(dest)?;
if *prefix == VendorPrefix::MS {
return dest.write_str("inline-flexbox");
} else {
return dest.write_str("inline-flex");
}
}
(DisplayOutside::Inline, DisplayInside::Box(prefix), false) => {
prefix.to_css(dest)?;
return dest.write_str("inline-box");
}
(DisplayOutside::Inline, DisplayInside::Grid, false) => {
return dest.write_str("inline-grid");
}
_ => {
let default_outside: DisplayOutside = match self.inside {
DisplayInside::Ruby => DisplayOutside::Inline,
_ => DisplayOutside::Block,
};
let mut needs_space = false;
if self.outside != default_outside
|| (self.inside == DisplayInside::Flow && !self.is_list_item)
{
self.outside.to_css(dest)?;
needs_space = true;
}
if self.inside != DisplayInside::Flow {
if needs_space {
dest.write_char(b' ')?;
}
self.inside.to_css(dest)?;
needs_space = true;
}
if self.is_list_item {
if needs_space {
dest.write_char(b' ')?;
}
dest.write_str("list-item")?;
}
Ok(())
}
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, css::DefineEnumProperty)]
pub enum DisplayOutside {
Block,
Inline,
RunIn,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum DisplayInside {
Flow,
FlowRoot,
Table,
Flex(VendorPrefix),
Box(VendorPrefix),
Grid,
Ruby,
}
impl DisplayInside {
pub(crate) fn parse(input: &mut Parser) -> css::Result<Self> {
let location = input.current_source_location();
let ident = input.expect_ident_cloned()?;
use bun_core::eql_case_insensitive_ascii as eq;
Ok(if eq(ident, b"flow", true) {
DisplayInside::Flow
} else if eq(ident, b"flow-root", true) {
DisplayInside::FlowRoot
} else if eq(ident, b"table", true) {
DisplayInside::Table
} else if eq(ident, b"flex", true) {
DisplayInside::Flex(VendorPrefix::NONE)
} else if eq(ident, b"-webkit-flex", true) {
DisplayInside::Flex(VendorPrefix::WEBKIT)
} else if eq(ident, b"-ms-flexbox", true) {
DisplayInside::Flex(VendorPrefix::MS)
} else if eq(ident, b"-webkit-box", true) {
DisplayInside::Box(VendorPrefix::WEBKIT)
} else if eq(ident, b"-moz-box", true) {
DisplayInside::Box(VendorPrefix::MOZ)
} else if eq(ident, b"grid", true) {
DisplayInside::Grid
} else if eq(ident, b"ruby", true) {
DisplayInside::Ruby
} else {
return Err(location.new_unexpected_token_error(css::Token::Ident(ident)));
})
}
pub(crate) fn to_css(self, dest: &mut Printer) -> Result<(), PrintErr> {
match self {
DisplayInside::Flow => dest.write_str("flow"),
DisplayInside::FlowRoot => dest.write_str("flow-root"),
DisplayInside::Table => dest.write_str("table"),
DisplayInside::Flex(prefix) => {
prefix.to_css(dest)?;
if prefix == VendorPrefix::MS {
dest.write_str("flexbox")
} else {
dest.write_str("flex")
}
}
DisplayInside::Box(prefix) => {
prefix.to_css(dest)?;
dest.write_str("box")
}
DisplayInside::Grid => dest.write_str("grid"),
DisplayInside::Ruby => dest.write_str("ruby"),
}
}
}