use cssparser::{Parser, Token, match_ignore_ascii_case};
use crate::layout::style::{
CssDescriptorKind, CssToken, FromCss, MakeComputed, ParseResult, TextWrapMode,
WhiteSpaceCollapse, tw::TailwindPropertyParser,
};
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[non_exhaustive]
pub struct WhiteSpace {
pub text_wrap_mode: TextWrapMode,
pub white_space_collapse: WhiteSpaceCollapse,
}
impl MakeComputed for WhiteSpace {}
impl TailwindPropertyParser for WhiteSpace {
fn parse_tw(token: &str) -> Option<Self> {
match_ignore_ascii_case! {token,
"normal" => Some(WhiteSpace::normal()),
"nowrap" => Some(WhiteSpace::no_wrap()),
"pre" => Some(WhiteSpace::pre()),
"pre-wrap" => Some(WhiteSpace::pre_wrap()),
"pre-line" => Some(WhiteSpace::pre_line()),
_ => None,
}
}
}
impl WhiteSpace {
pub const fn no_wrap() -> Self {
Self {
text_wrap_mode: TextWrapMode::NoWrap,
white_space_collapse: WhiteSpaceCollapse::Collapse,
}
}
pub const fn normal() -> Self {
Self {
text_wrap_mode: TextWrapMode::Wrap,
white_space_collapse: WhiteSpaceCollapse::Collapse,
}
}
pub const fn pre() -> Self {
Self {
text_wrap_mode: TextWrapMode::NoWrap,
white_space_collapse: WhiteSpaceCollapse::Preserve,
}
}
pub const fn pre_wrap() -> Self {
Self {
text_wrap_mode: TextWrapMode::Wrap,
white_space_collapse: WhiteSpaceCollapse::Preserve,
}
}
pub const fn pre_line() -> Self {
Self {
text_wrap_mode: TextWrapMode::Wrap,
white_space_collapse: WhiteSpaceCollapse::PreserveBreaks,
}
}
}
fn parse_white_space_keyword<'i>(input: &mut Parser<'i, '_>) -> ParseResult<'i, WhiteSpace> {
let location = input.current_source_location();
let ident = input.expect_ident()?;
match_ignore_ascii_case! {&ident,
"normal" => Ok(WhiteSpace::normal()),
"pre" => Ok(WhiteSpace::pre()),
"pre-wrap" => Ok(WhiteSpace::pre_wrap()),
"pre-line" => Ok(WhiteSpace::pre_line()),
_ => Err(location.new_basic_unexpected_token_error(Token::Ident(ident.clone())).into())
}
}
impl<'i> FromCss<'i> for WhiteSpace {
fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
if let Ok(ident) = input.try_parse(parse_white_space_keyword) {
return Ok(ident);
}
let mut text_wrap_mode = TextWrapMode::default();
let mut white_space_collapse = WhiteSpaceCollapse::default();
while !input.is_exhausted() {
if let Ok(value) = input.try_parse(TextWrapMode::from_css) {
text_wrap_mode = value;
continue;
}
if let Ok(value) = input.try_parse(WhiteSpaceCollapse::from_css) {
white_space_collapse = value;
continue;
}
return Err(input.new_error_for_next_token());
}
Ok(WhiteSpace {
text_wrap_mode,
white_space_collapse,
})
}
const VALID_TOKENS: &'static [CssToken] = &[
CssToken::Keyword("normal"),
CssToken::Keyword("pre"),
CssToken::Keyword("pre-wrap"),
CssToken::Keyword("pre-line"),
CssToken::Descriptor(CssDescriptorKind::TextWrapMode),
CssToken::Descriptor(CssDescriptorKind::WhiteSpaceCollapse),
];
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_white_space_no_wrap() {
assert_eq!(WhiteSpace::from_str("nowrap"), Ok(WhiteSpace::no_wrap()));
}
}