Skip to main content

takumi_css/style/properties/grid/
grid_repetition_count.rs

1use crate::style::unexpected_token;
2use cssparser::{Parser, Token};
3
4use crate::style::{CssSyntaxKind, CssToken, FromCss, ParseResult, ToCss};
5
6/// Represents grid track repetition keywords
7#[derive(Debug, Clone, Copy, PartialEq)]
8#[non_exhaustive]
9pub enum GridRepetitionKeyword {
10  /// Automatically fills the available space with as many tracks as possible
11  AutoFill,
12  /// Automatically fits as many tracks as possible while maintaining minimum size
13  AutoFit,
14}
15
16/// Represents a grid track repetition pattern
17#[derive(Debug, Clone, Copy, PartialEq)]
18#[non_exhaustive]
19pub enum GridRepetitionCount {
20  /// Keywords for auto-fill and auto-fit
21  Keyword(GridRepetitionKeyword),
22  /// Specifies an exact number of track repetitions
23  Count(u16),
24}
25
26impl From<GridRepetitionCount> for taffy::RepetitionCount {
27  fn from(repetition: GridRepetitionCount) -> Self {
28    match repetition {
29      GridRepetitionCount::Keyword(GridRepetitionKeyword::AutoFill) => {
30        taffy::RepetitionCount::AutoFill
31      }
32      GridRepetitionCount::Keyword(GridRepetitionKeyword::AutoFit) => {
33        taffy::RepetitionCount::AutoFit
34      }
35      GridRepetitionCount::Count(count) => taffy::RepetitionCount::Count(count),
36    }
37  }
38}
39
40impl<'i> FromCss<'i> for GridRepetitionCount {
41  fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
42    let location = input.current_source_location();
43    if let Ok(ident) = input.try_parse(Parser::expect_ident_cloned) {
44      let ident_str = ident.as_ref();
45      if ident_str.eq_ignore_ascii_case("auto-fill") {
46        return Ok(GridRepetitionCount::Keyword(
47          GridRepetitionKeyword::AutoFill,
48        ));
49      }
50      if ident_str.eq_ignore_ascii_case("auto-fit") {
51        return Ok(GridRepetitionCount::Keyword(GridRepetitionKeyword::AutoFit));
52      }
53      return Err(unexpected_token!(location, &Token::Ident(ident)));
54    }
55
56    let token = input.next()?;
57    match *token {
58      Token::Number {
59        int_value, value, ..
60      } => {
61        // Prefer integer value if provided
62        let count: i64 = if let Some(iv) = int_value {
63          iv as i64
64        } else {
65          value as i64
66        };
67        if count < 0 {
68          return Err::<Self, _>(
69            location
70              .new_basic_unexpected_token_error(token.clone())
71              .into(),
72          );
73        }
74        Ok(GridRepetitionCount::Count(count as u16))
75      }
76      _ => Err(unexpected_token!(location, token)),
77    }
78  }
79
80  const VALID_TOKENS: &'static [CssToken] = &[
81    CssToken::Keyword("auto-fill"),
82    CssToken::Keyword("auto-fit"),
83    CssToken::Syntax(CssSyntaxKind::Number),
84  ];
85}
86
87impl ToCss for GridRepetitionKeyword {
88  fn to_css<W: std::fmt::Write>(&self, dest: &mut W) -> std::fmt::Result {
89    match self {
90      Self::AutoFill => dest.write_str("auto-fill"),
91      Self::AutoFit => dest.write_str("auto-fit"),
92    }
93  }
94}
95
96impl ToCss for GridRepetitionCount {
97  fn to_css<W: std::fmt::Write>(&self, dest: &mut W) -> std::fmt::Result {
98    match self {
99      Self::Keyword(kw) => kw.to_css(dest),
100      Self::Count(c) => write!(dest, "{}", c),
101    }
102  }
103}
104
105#[cfg(test)]
106mod tests {
107  use super::*;
108
109  #[test]
110  fn test_parse_repetition_count() {
111    assert_eq!(
112      GridRepetitionCount::from_str("auto-fill"),
113      Ok(GridRepetitionCount::Keyword(
114        GridRepetitionKeyword::AutoFill
115      ))
116    );
117
118    assert_eq!(
119      GridRepetitionCount::from_str("auto-fit"),
120      Ok(GridRepetitionCount::Keyword(GridRepetitionKeyword::AutoFit))
121    );
122
123    assert_eq!(
124      GridRepetitionCount::from_str("3"),
125      Ok(GridRepetitionCount::Count(3))
126    );
127  }
128}