use cssparser::Parser;
use crate::layout::style::{CssToken, FromCss, MakeComputed, ParseResult};
use crate::rendering::Sizing;
use super::{GridRepeatTrack, GridRepetitionCount, GridTrackSize};
pub type GridTemplateComponents = Vec<GridTemplateComponent>;
#[derive(Debug, Clone, PartialEq)]
pub enum GridTemplateComponent {
LineNames(Vec<String>),
Single(GridTrackSize),
Repeat(GridRepetitionCount, Vec<GridRepeatTrack>),
}
impl MakeComputed for GridTemplateComponent {
fn make_computed(&mut self, sizing: &Sizing) {
match self {
GridTemplateComponent::Single(size) => size.make_computed(sizing),
GridTemplateComponent::Repeat(_, tracks) => {
for track in tracks.iter_mut() {
track.make_computed(sizing);
}
}
_ => {}
}
}
}
impl<'i> FromCss<'i> for GridTemplateComponent {
fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
if input.try_parse(Parser::expect_square_bracket_block).is_ok() {
let mut names: Vec<String> = Vec::new();
input.parse_nested_block(|i| {
while let Ok(name) = i.try_parse(Parser::expect_ident_cloned) {
names.push(name.as_ref().to_owned());
}
Ok(())
})?;
return Ok(GridTemplateComponent::LineNames(names));
}
if input
.try_parse(|i| i.expect_function_matching("repeat"))
.is_ok()
{
return input.parse_nested_block(|input| {
let repetition = GridRepetitionCount::from_css(input)?;
input.expect_comma()?;
let mut tracks: Vec<GridRepeatTrack> = Vec::new();
let mut pending_leading_names: Vec<String> = Vec::new();
loop {
let mut names: Vec<String> = std::mem::take(&mut pending_leading_names);
while input.try_parse(Parser::expect_square_bracket_block).is_ok() {
input.parse_nested_block(|i| {
while let Ok(name) = i.try_parse(Parser::expect_ident_cloned) {
names.push(name.as_ref().to_owned());
}
Ok(())
})?;
}
let size = if let Ok(size) = input.try_parse(GridTrackSize::from_css) {
size
} else {
break;
};
while input.try_parse(Parser::expect_square_bracket_block).is_ok() {
input.parse_nested_block(|i| {
while let Ok(name) = i.try_parse(Parser::expect_ident_cloned) {
pending_leading_names.push(name.as_ref().to_owned());
}
Ok(())
})?;
}
tracks.push(GridRepeatTrack {
size,
names,
end_names: None,
});
}
if !pending_leading_names.is_empty()
&& let Some(last) = tracks.last_mut()
{
last.end_names = Some(std::mem::take(&mut pending_leading_names));
}
Ok(GridTemplateComponent::Repeat(repetition, tracks))
});
}
let size = GridTrackSize::from_css(input)?;
Ok(GridTemplateComponent::Single(size))
}
fn valid_tokens() -> &'static [CssToken] {
&[
CssToken::Token("line-names"),
CssToken::Token("repeat()"),
CssToken::Token("minmax()"),
CssToken::Token("length"),
]
}
}
impl<'i> FromCss<'i> for GridTemplateComponents {
fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
let mut components = Vec::new();
while let Ok(component) = GridTemplateComponent::from_css(input) {
components.push(component);
}
Ok(components)
}
fn valid_tokens() -> &'static [CssToken] {
GridTemplateComponent::valid_tokens()
}
}
#[cfg(test)]
mod tests {
use crate::layout::style::{GridLength, GridRepetitionKeyword};
use super::*;
#[test]
fn test_parse_template_component_repeat() {
assert_eq!(
GridTemplateComponent::from_str("repeat(auto-fill, [a] 1fr [b] 2fr)"),
Ok(GridTemplateComponent::Repeat(
GridRepetitionCount::Keyword(GridRepetitionKeyword::AutoFill),
vec![
GridRepeatTrack {
names: vec!["a".to_string()],
size: GridTrackSize::Fixed(GridLength::Fr(1.0)),
end_names: None
},
GridRepeatTrack {
names: vec!["b".to_string()],
size: GridTrackSize::Fixed(GridLength::Fr(2.0)),
end_names: None
}
]
))
);
}
}