takumi_css/style/properties/grid/
grid_template_component.rs1use std::mem::take;
2
3use cssparser::Parser;
4
5use super::write_space_separated;
6use crate::style::{
7 CssDescriptorKind, CssSyntaxKind, CssToken, FromCss, GridRepeatTrack, GridRepetitionCount,
8 GridTrackSize, MakeComputed, ParseResult, SizingContext, ToCss,
9};
10
11pub type GridTemplateComponents = Vec<GridTemplateComponent>;
12
13pub(crate) fn parse_line_names<'i>(input: &mut Parser<'i, '_>) -> ParseResult<'i, Vec<String>> {
15 input.parse_nested_block(|i| {
16 let mut names = Vec::new();
17 while let Ok(name) = i.try_parse(Parser::expect_ident_cloned) {
18 names.push(name.as_ref().to_owned());
19 }
20 Ok(names)
21 })
22}
23
24#[derive(Debug, Clone, PartialEq)]
26#[non_exhaustive]
27pub enum GridTemplateComponent {
28 LineNames(Vec<String>),
30 Single(GridTrackSize),
32 Repeat(GridRepetitionCount, Vec<GridRepeatTrack>),
35}
36
37impl MakeComputed for GridTemplateComponent {
38 fn make_computed(&mut self, sizing: &SizingContext) {
39 match self {
40 GridTemplateComponent::Single(size) => size.make_computed(sizing),
41 GridTemplateComponent::Repeat(_, tracks) => {
42 for track in tracks.iter_mut() {
43 track.make_computed(sizing);
44 }
45 }
46 _ => {}
47 }
48 }
49}
50
51impl<'i> FromCss<'i> for GridTemplateComponent {
52 fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
53 if input.try_parse(Parser::expect_square_bracket_block).is_ok() {
55 return Ok(GridTemplateComponent::LineNames(parse_line_names(input)?));
56 }
57
58 if input
59 .try_parse(|i| i.expect_function_matching("repeat"))
60 .is_ok()
61 {
62 return input.parse_nested_block(|input| {
63 let repetition = GridRepetitionCount::from_css(input)?;
64 input.expect_comma()?;
65
66 let mut tracks: Vec<GridRepeatTrack> = Vec::new();
67 let mut pending_leading_names: Vec<String> = Vec::new();
69 loop {
70 let mut names: Vec<String> = take(&mut pending_leading_names);
72
73 while input.try_parse(Parser::expect_square_bracket_block).is_ok() {
75 names.extend(parse_line_names(input)?);
76 }
77
78 let size = if let Ok(size) = input.try_parse(GridTrackSize::from_css) {
80 size
81 } else {
82 break;
83 };
84
85 while input.try_parse(Parser::expect_square_bracket_block).is_ok() {
87 pending_leading_names.extend(parse_line_names(input)?);
88 }
89
90 tracks.push(GridRepeatTrack {
91 size,
92 names,
93 end_names: None,
94 });
95 }
96
97 if tracks.is_empty() {
98 return Err(input.new_error_for_next_token());
99 }
100
101 if !pending_leading_names.is_empty()
103 && let Some(last) = tracks.last_mut()
104 {
105 last.end_names = Some(take(&mut pending_leading_names));
106 }
107
108 Ok(GridTemplateComponent::Repeat(repetition, tracks))
109 });
110 }
111
112 let size = GridTrackSize::from_css(input)?;
114 Ok(GridTemplateComponent::Single(size))
115 }
116
117 const VALID_TOKENS: &'static [CssToken] = &[
118 CssToken::Syntax(CssSyntaxKind::LineNames),
119 CssToken::Descriptor(CssDescriptorKind::RepeatFn),
120 CssToken::Descriptor(CssDescriptorKind::MinmaxFn),
121 CssToken::Syntax(CssSyntaxKind::Length),
122 ];
123}
124
125impl<'i> FromCss<'i> for GridTemplateComponents {
126 fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
127 let mut components = Vec::new();
128 while let Ok(component) = GridTemplateComponent::from_css(input) {
129 components.push(component);
130 }
131 Ok(components)
132 }
133
134 const VALID_TOKENS: &'static [CssToken] = GridTemplateComponent::VALID_TOKENS;
135}
136
137pub(crate) fn collect_components_and_names(
138 components: &[GridTemplateComponent],
139 sizing: &SizingContext,
140) -> (Vec<taffy::GridTemplateComponent<String>>, Vec<Vec<String>>) {
141 let mut track_components = Vec::new();
142 let mut line_name_sets = Vec::new();
143 let mut pending_line_names = Vec::new();
144
145 for component in components {
146 match component {
147 GridTemplateComponent::LineNames(names) => {
148 if !names.is_empty() {
149 pending_line_names.extend_from_slice(names);
150 }
151 }
152 GridTemplateComponent::Single(track_size) => {
153 line_name_sets.push(take(&mut pending_line_names));
154 track_components.push(taffy::GridTemplateComponent::Single(
155 track_size.to_min_max(sizing),
156 ));
157 }
158 GridTemplateComponent::Repeat(repetition, tracks) => {
159 line_name_sets.push(take(&mut pending_line_names));
160
161 let track_sizes = tracks
162 .iter()
163 .map(|track| track.size.to_min_max(sizing))
164 .collect();
165 let mut inner_line_names = tracks
166 .iter()
167 .map(|track| track.names.to_owned())
168 .collect::<Vec<_>>();
169 inner_line_names.push(
170 tracks
171 .last()
172 .and_then(|track| track.end_names.clone())
173 .unwrap_or_default(),
174 );
175
176 track_components.push(taffy::GridTemplateComponent::Repeat(
177 taffy::GridTemplateRepetition {
178 count: (*repetition).into(),
179 tracks: track_sizes,
180 line_names: inner_line_names,
181 },
182 ));
183 }
184 }
185 }
186
187 line_name_sets.push(pending_line_names);
188
189 (track_components, line_name_sets)
190}
191
192impl ToCss for GridTemplateComponent {
193 fn to_css<W: std::fmt::Write>(&self, dest: &mut W) -> std::fmt::Result {
194 match self {
195 Self::LineNames(names) => {
196 dest.write_str("[")?;
197 write_space_separated(dest, names)?;
198 dest.write_str("]")
199 }
200 Self::Single(size) => size.to_css(dest),
201 Self::Repeat(count, tracks) => {
202 dest.write_str("repeat(")?;
203 count.to_css(dest)?;
204 dest.write_str(", ")?;
205 let mut first = true;
206 for track in tracks {
207 if !first {
208 dest.write_str(" ")?;
209 }
210 first = false;
211 track.to_css(dest)?;
212 }
213 dest.write_str(")")
214 }
215 }
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use crate::style::{GridLength, GridRepetitionKeyword};
222
223 use super::*;
224
225 #[test]
226 fn test_parse_template_component_repeat() {
227 assert_eq!(
228 GridTemplateComponent::from_str("repeat(auto-fill, [a] 1fr [b] 2fr)"),
229 Ok(GridTemplateComponent::Repeat(
230 GridRepetitionCount::Keyword(GridRepetitionKeyword::AutoFill),
231 vec![
232 GridRepeatTrack {
233 names: vec!["a".to_string()],
234 size: GridTrackSize::Fixed(GridLength::Fr(1.0)),
235 end_names: None
236 },
237 GridRepeatTrack {
238 names: vec!["b".to_string()],
239 size: GridTrackSize::Fixed(GridLength::Fr(2.0)),
240 end_names: None
241 }
242 ]
243 ))
244 );
245 }
246
247 #[test]
248 fn test_parse_repeat_without_track_size_is_error() {
249 assert!(GridTemplateComponent::from_str("repeat(2, [a])").is_err());
250 }
251}