use crate::layout::style::unexpected_token;
use cssparser::{Parser, Token};
use crate::layout::style::{
Animatable, CssSyntaxKind, CssToken, FromCss, MakeComputed, ParseResult,
tw::TailwindPropertyParser,
};
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum GridPlacement {
Keyword(GridPlacementKeyword),
Span(GridPlacementSpan),
Line(i16),
Named(String),
}
impl MakeComputed for GridPlacement {}
impl Animatable for GridPlacement {}
impl Default for GridPlacement {
fn default() -> Self {
Self::auto()
}
}
impl GridPlacement {
pub const fn auto() -> Self {
Self::Keyword(GridPlacementKeyword::Auto)
}
pub const fn span(span: u16) -> Self {
Self::Span(GridPlacementSpan::Span(span))
}
}
impl TailwindPropertyParser for GridPlacement {
fn parse_tw(token: &str) -> Option<Self> {
token.parse::<i16>().map(Self::Line).ok()
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq)]
#[non_exhaustive]
pub enum GridPlacementKeyword {
#[default]
Auto,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum GridPlacementSpan {
Span(u16),
}
impl<'i> FromCss<'i> for GridPlacementSpan {
fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
Ok(
input
.expect_integer()
.map(|n| GridPlacementSpan::Span(n.max(1) as u16))?,
)
}
const VALID_TOKENS: &'static [CssToken] = &[CssToken::Syntax(CssSyntaxKind::Integer)];
}
impl TailwindPropertyParser for GridPlacementSpan {
fn parse_tw(token: &str) -> Option<Self> {
token.parse::<u16>().map(Self::Span).ok()
}
}
impl From<GridPlacement> for taffy::GridPlacement {
fn from(placement: GridPlacement) -> Self {
match placement {
GridPlacement::Keyword(GridPlacementKeyword::Auto) => taffy::GridPlacement::Auto,
GridPlacement::Line(line) => taffy::GridPlacement::Line(line.into()),
GridPlacement::Span(GridPlacementSpan::Span(span)) => taffy::GridPlacement::Span(span),
GridPlacement::Named(_) => taffy::GridPlacement::Auto,
}
}
}
impl<'i> FromCss<'i> for GridPlacement {
fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
if let Ok(ident) = input.try_parse(Parser::expect_ident_cloned) {
if ident.eq_ignore_ascii_case("auto") {
return Ok(GridPlacement::auto());
}
if ident.eq_ignore_ascii_case("span") {
if let Ok(span) = input.try_parse(GridPlacementSpan::from_css) {
return Ok(GridPlacement::Span(span));
}
if let Ok(_name) = input.try_parse(Parser::expect_ident_cloned) {
return Ok(GridPlacement::span(1));
}
return Err(unexpected_token!(
input.current_source_location(),
input.next()?,
));
}
return Ok(GridPlacement::Named(ident.to_string()));
}
let location = input.current_source_location();
let token = input.next()?;
match *token {
Token::Number {
int_value, value, ..
} => {
let v: i32 = int_value.unwrap_or(value as i32);
Ok(GridPlacement::Line(v as i16))
}
_ => Err(unexpected_token!(location, token)),
}
}
const VALID_TOKENS: &'static [CssToken] = &[
CssToken::Keyword("auto"),
CssToken::Keyword("span"),
CssToken::Syntax(CssSyntaxKind::Number),
CssToken::Syntax(CssSyntaxKind::Ident),
];
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_placement() {
assert_eq!(GridPlacement::from_str("auto"), Ok(GridPlacement::auto()));
assert_eq!(
GridPlacement::from_str("span 2"),
Ok(GridPlacement::span(2))
);
assert_eq!(
GridPlacement::from_str("span name"),
Ok(GridPlacement::span(1))
);
assert_eq!(GridPlacement::from_str("3"), Ok(GridPlacement::Line(3)));
assert_eq!(GridPlacement::from_str("-1"), Ok(GridPlacement::Line(-1)));
assert_eq!(
GridPlacement::from_str("header"),
Ok(GridPlacement::Named("header".to_string()))
);
}
}