takumi 1.7.0

Render UI component trees to images.
Documentation
use crate::layout::style::unexpected_token;
use cssparser::{Parser, Token};

use crate::layout::style::{CssSyntaxKind, CssToken, FromCss, ParseResult, ToCss};

/// Represents grid track repetition keywords
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum GridRepetitionKeyword {
  /// Automatically fills the available space with as many tracks as possible
  AutoFill,
  /// Automatically fits as many tracks as possible while maintaining minimum size
  AutoFit,
}

/// Represents a grid track repetition pattern
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum GridRepetitionCount {
  /// Keywords for auto-fill and auto-fit
  Keyword(GridRepetitionKeyword),
  /// Specifies an exact number of track repetitions
  Count(u16),
}

impl From<GridRepetitionCount> for taffy::RepetitionCount {
  fn from(repetition: GridRepetitionCount) -> Self {
    match repetition {
      GridRepetitionCount::Keyword(GridRepetitionKeyword::AutoFill) => {
        taffy::RepetitionCount::AutoFill
      }
      GridRepetitionCount::Keyword(GridRepetitionKeyword::AutoFit) => {
        taffy::RepetitionCount::AutoFit
      }
      GridRepetitionCount::Count(count) => taffy::RepetitionCount::Count(count),
    }
  }
}

impl<'i> FromCss<'i> for GridRepetitionCount {
  fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
    let location = input.current_source_location();
    if let Ok(ident) = input.try_parse(Parser::expect_ident_cloned) {
      let ident_str = ident.as_ref();
      if ident_str.eq_ignore_ascii_case("auto-fill") {
        return Ok(GridRepetitionCount::Keyword(
          GridRepetitionKeyword::AutoFill,
        ));
      }
      if ident_str.eq_ignore_ascii_case("auto-fit") {
        return Ok(GridRepetitionCount::Keyword(GridRepetitionKeyword::AutoFit));
      }
      return Err(unexpected_token!(location, &Token::Ident(ident)));
    }

    let token = input.next()?;
    match *token {
      Token::Number {
        int_value, value, ..
      } => {
        // Prefer integer value if provided
        let count: i64 = if let Some(iv) = int_value {
          iv as i64
        } else {
          value as i64
        };
        if count < 0 {
          return Err::<Self, _>(
            location
              .new_basic_unexpected_token_error(token.clone())
              .into(),
          );
        }
        Ok(GridRepetitionCount::Count(count as u16))
      }
      _ => Err(unexpected_token!(location, token)),
    }
  }

  const VALID_TOKENS: &'static [CssToken] = &[
    CssToken::Keyword("auto-fill"),
    CssToken::Keyword("auto-fit"),
    CssToken::Syntax(CssSyntaxKind::Number),
  ];
}

impl ToCss for GridRepetitionKeyword {
  fn to_css<W: std::fmt::Write>(&self, dest: &mut W) -> std::fmt::Result {
    match self {
      Self::AutoFill => dest.write_str("auto-fill"),
      Self::AutoFit => dest.write_str("auto-fit"),
    }
  }
}

impl ToCss for GridRepetitionCount {
  fn to_css<W: std::fmt::Write>(&self, dest: &mut W) -> std::fmt::Result {
    match self {
      Self::Keyword(kw) => kw.to_css(dest),
      Self::Count(c) => write!(dest, "{}", c),
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn test_parse_repetition_count() {
    assert_eq!(
      GridRepetitionCount::from_str("auto-fill"),
      Ok(GridRepetitionCount::Keyword(
        GridRepetitionKeyword::AutoFill
      ))
    );

    assert_eq!(
      GridRepetitionCount::from_str("auto-fit"),
      Ok(GridRepetitionCount::Keyword(GridRepetitionKeyword::AutoFit))
    );

    assert_eq!(
      GridRepetitionCount::from_str("3"),
      Ok(GridRepetitionCount::Count(3))
    );
  }
}