1use alloc::{
4 boxed::Box,
5 string::{String, ToString},
6 vec::Vec,
7};
8
9use crate::{
10 corety::AzString,
11 format_rust_code::FormatAsRustCode,
12 impl_vec, impl_vec_clone, impl_vec_debug, impl_vec_eq, impl_vec_hash, impl_vec_mut,
13 impl_vec_ord, impl_vec_partialeq, impl_vec_partialord,
14 props::{basic::pixel::PixelValue, formatter::PrintAsCssValue},
15};
16
17#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
21#[repr(C)]
22pub struct GridMinMax {
23 pub min: Box<GridTrackSizing>,
24 pub max: Box<GridTrackSizing>,
25}
26
27impl core::fmt::Debug for GridMinMax {
28 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
29 write!(
30 f,
31 "minmax({}, {})",
32 self.min.print_as_css_value(),
33 self.max.print_as_css_value()
34 )
35 }
36}
37
38#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
40#[repr(C, u8)]
41pub enum GridTrackSizing {
42 Fixed(PixelValue),
44 Fr(i32),
46 MinContent,
48 MaxContent,
50 Auto,
52 MinMax(GridMinMax),
54 FitContent(PixelValue),
56}
57
58impl core::fmt::Debug for GridTrackSizing {
59 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
60 write!(f, "{}", self.print_as_css_value())
61 }
62}
63
64impl Default for GridTrackSizing {
65 fn default() -> Self {
66 GridTrackSizing::Auto
67 }
68}
69
70impl PrintAsCssValue for GridTrackSizing {
71 fn print_as_css_value(&self) -> String {
72 match self {
73 GridTrackSizing::Fixed(px) => px.print_as_css_value(),
74 GridTrackSizing::Fr(f) => format!("{}fr", f),
75 GridTrackSizing::MinContent => "min-content".to_string(),
76 GridTrackSizing::MaxContent => "max-content".to_string(),
77 GridTrackSizing::Auto => "auto".to_string(),
78 GridTrackSizing::MinMax(minmax) => {
79 format!(
80 "minmax({}, {})",
81 minmax.min.print_as_css_value(),
82 minmax.max.print_as_css_value()
83 )
84 }
85 GridTrackSizing::FitContent(size) => {
86 format!("fit-content({})", size.print_as_css_value())
87 }
88 }
89 }
90}
91
92impl_vec!(
94 GridTrackSizing,
95 GridTrackSizingVec,
96 GridTrackSizingVecDestructor,
97 GridTrackSizingVecDestructorType
98);
99impl_vec_clone!(
100 GridTrackSizing,
101 GridTrackSizingVec,
102 GridTrackSizingVecDestructor
103);
104impl_vec_debug!(GridTrackSizing, GridTrackSizingVec);
105impl_vec_partialeq!(GridTrackSizing, GridTrackSizingVec);
106impl_vec_eq!(GridTrackSizing, GridTrackSizingVec);
107impl_vec_partialord!(GridTrackSizing, GridTrackSizingVec);
108impl_vec_ord!(GridTrackSizing, GridTrackSizingVec);
109impl_vec_hash!(GridTrackSizing, GridTrackSizingVec);
110impl_vec_mut!(GridTrackSizing, GridTrackSizingVec);
111
112#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
114#[repr(C)]
115pub struct GridTemplate {
116 pub tracks: GridTrackSizingVec,
117}
118
119impl core::fmt::Debug for GridTemplate {
120 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
121 write!(f, "{}", self.print_as_css_value())
122 }
123}
124
125impl Default for GridTemplate {
126 fn default() -> Self {
127 GridTemplate {
128 tracks: GridTrackSizingVec::from_vec(Vec::new()),
129 }
130 }
131}
132
133impl PrintAsCssValue for GridTemplate {
134 fn print_as_css_value(&self) -> String {
135 let tracks_slice = self.tracks.as_ref();
136 if tracks_slice.is_empty() {
137 "none".to_string()
138 } else {
139 tracks_slice
140 .iter()
141 .map(|t| t.print_as_css_value())
142 .collect::<Vec<_>>()
143 .join(" ")
144 }
145 }
146}
147
148#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
153#[repr(C)]
154pub struct GridAutoTracks {
155 pub tracks: GridTrackSizingVec,
156}
157
158impl core::fmt::Debug for GridAutoTracks {
159 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
160 write!(f, "{}", self.print_as_css_value())
161 }
162}
163
164impl Default for GridAutoTracks {
165 fn default() -> Self {
166 GridAutoTracks {
167 tracks: GridTrackSizingVec::from_vec(Vec::new()),
168 }
169 }
170}
171
172impl PrintAsCssValue for GridAutoTracks {
173 fn print_as_css_value(&self) -> String {
174 let tracks_slice = self.tracks.as_ref();
175 if tracks_slice.is_empty() {
176 "auto".to_string()
177 } else {
178 tracks_slice
179 .iter()
180 .map(|t| t.print_as_css_value())
181 .collect::<Vec<_>>()
182 .join(" ")
183 }
184 }
185}
186
187impl From<GridTemplate> for GridAutoTracks {
188 fn from(template: GridTemplate) -> Self {
189 GridAutoTracks {
190 tracks: template.tracks,
191 }
192 }
193}
194
195#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
199#[repr(C)]
200pub struct NamedGridLine {
201 pub grid_line_name: AzString,
202 pub span_count: i32,
204}
205
206impl NamedGridLine {
207 pub fn create(name: AzString, span: Option<i32>) -> Self {
208 Self {
209 grid_line_name: name,
210 span_count: span.unwrap_or(0),
211 }
212 }
213
214 pub fn span(&self) -> Option<i32> {
215 if self.span_count == 0 {
216 None
217 } else {
218 Some(self.span_count)
219 }
220 }
221}
222
223#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
225#[repr(C, u8)]
226pub enum GridLine {
227 Auto,
229 Line(i32),
231 Named(NamedGridLine),
233 Span(i32),
235}
236
237impl core::fmt::Debug for GridLine {
238 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
239 write!(f, "{}", self.print_as_css_value())
240 }
241}
242
243impl Default for GridLine {
244 fn default() -> Self {
245 GridLine::Auto
246 }
247}
248
249impl PrintAsCssValue for GridLine {
250 fn print_as_css_value(&self) -> String {
251 match self {
252 GridLine::Auto => "auto".to_string(),
253 GridLine::Line(n) => n.to_string(),
254 GridLine::Named(named) => {
255 if named.span_count == 0 {
256 named.grid_line_name.as_str().to_string()
257 } else {
258 format!("{} {}", named.grid_line_name.as_str(), named.span_count)
259 }
260 }
261 GridLine::Span(n) => format!("span {}", n),
262 }
263 }
264}
265
266#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
268#[repr(C)]
269pub struct GridPlacement {
270 pub grid_start: GridLine,
271 pub grid_end: GridLine,
272}
273
274impl core::fmt::Debug for GridPlacement {
275 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
276 write!(f, "{}", self.print_as_css_value())
277 }
278}
279
280impl Default for GridPlacement {
281 fn default() -> Self {
282 GridPlacement {
283 grid_start: GridLine::Auto,
284 grid_end: GridLine::Auto,
285 }
286 }
287}
288
289impl PrintAsCssValue for GridPlacement {
290 fn print_as_css_value(&self) -> String {
291 if self.grid_end == GridLine::Auto {
292 self.grid_start.print_as_css_value()
293 } else {
294 format!(
295 "{} / {}",
296 self.grid_start.print_as_css_value(),
297 self.grid_end.print_as_css_value()
298 )
299 }
300 }
301}
302
303#[cfg(feature = "parser")]
304#[derive(Clone, PartialEq)]
305pub enum GridParseError<'a> {
306 InvalidValue(&'a str),
307}
308
309#[cfg(feature = "parser")]
310impl_debug_as_display!(GridParseError<'a>);
311#[cfg(feature = "parser")]
312impl_display! { GridParseError<'a>, {
313 InvalidValue(e) => format!("Invalid grid value: \"{}\"", e),
314}}
315
316#[cfg(feature = "parser")]
317#[derive(Debug, Clone, PartialEq)]
318pub enum GridParseErrorOwned {
319 InvalidValue(String),
320}
321
322#[cfg(feature = "parser")]
323impl<'a> GridParseError<'a> {
324 pub fn to_contained(&self) -> GridParseErrorOwned {
325 match self {
326 GridParseError::InvalidValue(s) => GridParseErrorOwned::InvalidValue(s.to_string()),
327 }
328 }
329}
330
331#[cfg(feature = "parser")]
332impl GridParseErrorOwned {
333 pub fn to_shared<'a>(&'a self) -> GridParseError<'a> {
334 match self {
335 GridParseErrorOwned::InvalidValue(s) => GridParseError::InvalidValue(s.as_str()),
336 }
337 }
338}
339
340#[cfg(feature = "parser")]
341pub fn parse_grid_template<'a>(input: &'a str) -> Result<GridTemplate, GridParseError<'a>> {
342 use crate::props::basic::pixel::parse_pixel_value;
343
344 let input = input.trim();
345
346 if input == "none" {
347 return Ok(GridTemplate::default());
348 }
349
350 let mut tracks = Vec::new();
351 let mut current = String::new();
352 let mut paren_depth = 0;
353
354 for ch in input.chars() {
355 match ch {
356 '(' => {
357 paren_depth += 1;
358 current.push(ch);
359 }
360 ')' => {
361 paren_depth -= 1;
362 current.push(ch);
363 }
364 ' ' if paren_depth == 0 => {
365 if !current.trim().is_empty() {
366 let track_str = current.trim().to_string();
367 tracks.push(
368 parse_grid_track_owned(&track_str)
369 .map_err(|_| GridParseError::InvalidValue(input))?,
370 );
371 current.clear();
372 }
373 }
374 _ => current.push(ch),
375 }
376 }
377
378 if !current.trim().is_empty() {
379 let track_str = current.trim().to_string();
380 tracks.push(
381 parse_grid_track_owned(&track_str).map_err(|_| GridParseError::InvalidValue(input))?,
382 );
383 }
384
385 Ok(GridTemplate {
386 tracks: GridTrackSizingVec::from_vec(tracks),
387 })
388}
389
390#[cfg(feature = "parser")]
391fn parse_grid_track_owned(input: &str) -> Result<GridTrackSizing, ()> {
392 use crate::props::basic::pixel::parse_pixel_value;
393
394 let input = input.trim();
395
396 if input == "auto" {
397 return Ok(GridTrackSizing::Auto);
398 }
399
400 if input == "min-content" {
401 return Ok(GridTrackSizing::MinContent);
402 }
403
404 if input == "max-content" {
405 return Ok(GridTrackSizing::MaxContent);
406 }
407
408 if input.ends_with("fr") {
409 let num_str = &input[..input.len() - 2].trim();
410 if let Ok(num) = num_str.parse::<f32>() {
411 return Ok(GridTrackSizing::Fr((num * 100.0) as i32));
412 }
413 return Err(());
414 }
415
416 if input.starts_with("minmax(") && input.ends_with(')') {
417 let content = &input[7..input.len() - 1];
418 let parts: Vec<&str> = content.split(',').collect();
419 if parts.len() == 2 {
420 let min = parse_grid_track_owned(parts[0].trim())?;
421 let max = parse_grid_track_owned(parts[1].trim())?;
422 return Ok(GridTrackSizing::MinMax(GridMinMax {
423 min: Box::new(min),
424 max: Box::new(max),
425 }));
426 }
427 return Err(());
428 }
429
430 if input.starts_with("fit-content(") && input.ends_with(')') {
431 let size_str = &input[12..input.len() - 1].trim();
432 if let Ok(size) = parse_pixel_value(size_str) {
433 return Ok(GridTrackSizing::FitContent(size));
434 }
435 return Err(());
436 }
437
438 if let Ok(px) = parse_pixel_value(input) {
440 return Ok(GridTrackSizing::Fixed(px));
441 }
442
443 Err(())
444}
445
446#[cfg(feature = "parser")]
447pub fn parse_grid_placement<'a>(input: &'a str) -> Result<GridPlacement, GridParseError<'a>> {
448 let input = input.trim();
449
450 if input == "auto" {
451 return Ok(GridPlacement::default());
452 }
453
454 let parts: Vec<&str> = input.split('/').map(|s| s.trim()).collect();
456
457 let grid_start =
458 parse_grid_line_owned(parts[0]).map_err(|_| GridParseError::InvalidValue(input))?;
459 let grid_end = if parts.len() > 1 {
460 parse_grid_line_owned(parts[1]).map_err(|_| GridParseError::InvalidValue(input))?
461 } else {
462 GridLine::Auto
463 };
464
465 Ok(GridPlacement {
466 grid_start,
467 grid_end,
468 })
469}
470
471#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
475#[repr(C)]
476pub enum LayoutGridAutoFlow {
477 Row,
478 Column,
479 RowDense,
480 ColumnDense,
481}
482
483impl Default for LayoutGridAutoFlow {
484 fn default() -> Self {
485 LayoutGridAutoFlow::Row
486 }
487}
488
489impl crate::props::formatter::PrintAsCssValue for LayoutGridAutoFlow {
490 fn print_as_css_value(&self) -> alloc::string::String {
491 match self {
492 LayoutGridAutoFlow::Row => "row".to_string(),
493 LayoutGridAutoFlow::Column => "column".to_string(),
494 LayoutGridAutoFlow::RowDense => "row dense".to_string(),
495 LayoutGridAutoFlow::ColumnDense => "column dense".to_string(),
496 }
497 }
498}
499
500#[cfg(feature = "parser")]
501#[derive(Clone, PartialEq)]
502pub enum GridAutoFlowParseError<'a> {
503 InvalidValue(&'a str),
504}
505
506#[cfg(feature = "parser")]
507impl_debug_as_display!(GridAutoFlowParseError<'a>);
508#[cfg(feature = "parser")]
509impl_display! { GridAutoFlowParseError<'a>, {
510 InvalidValue(e) => format!("Invalid grid-auto-flow value: \"{}\"", e),
511}}
512
513#[cfg(feature = "parser")]
514#[derive(Debug, Clone, PartialEq)]
515pub enum GridAutoFlowParseErrorOwned {
516 InvalidValue(alloc::string::String),
517}
518
519#[cfg(feature = "parser")]
520impl<'a> GridAutoFlowParseError<'a> {
521 pub fn to_contained(&self) -> GridAutoFlowParseErrorOwned {
522 match self {
523 GridAutoFlowParseError::InvalidValue(s) => {
524 GridAutoFlowParseErrorOwned::InvalidValue(s.to_string())
525 }
526 }
527 }
528}
529
530#[cfg(feature = "parser")]
531impl GridAutoFlowParseErrorOwned {
532 pub fn to_shared<'a>(&'a self) -> GridAutoFlowParseError<'a> {
533 match self {
534 GridAutoFlowParseErrorOwned::InvalidValue(s) => {
535 GridAutoFlowParseError::InvalidValue(s.as_str())
536 }
537 }
538 }
539}
540
541#[cfg(feature = "parser")]
542pub fn parse_layout_grid_auto_flow<'a>(
543 input: &'a str,
544) -> Result<LayoutGridAutoFlow, GridAutoFlowParseError<'a>> {
545 match input.trim() {
546 "row" => Ok(LayoutGridAutoFlow::Row),
547 "column" => Ok(LayoutGridAutoFlow::Column),
548 "row dense" => Ok(LayoutGridAutoFlow::RowDense),
549 "column dense" => Ok(LayoutGridAutoFlow::ColumnDense),
550 "dense" => Ok(LayoutGridAutoFlow::RowDense),
551 _ => Err(GridAutoFlowParseError::InvalidValue(input)),
552 }
553}
554
555#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
559#[repr(C)]
560pub enum LayoutJustifySelf {
561 Auto,
562 Start,
563 End,
564 Center,
565 Stretch,
566}
567
568impl Default for LayoutJustifySelf {
569 fn default() -> Self {
570 Self::Auto
571 }
572}
573
574impl crate::props::formatter::PrintAsCssValue for LayoutJustifySelf {
575 fn print_as_css_value(&self) -> alloc::string::String {
576 match self {
577 LayoutJustifySelf::Auto => "auto".to_string(),
578 LayoutJustifySelf::Start => "start".to_string(),
579 LayoutJustifySelf::End => "end".to_string(),
580 LayoutJustifySelf::Center => "center".to_string(),
581 LayoutJustifySelf::Stretch => "stretch".to_string(),
582 }
583 }
584}
585
586#[cfg(feature = "parser")]
587#[derive(Clone, PartialEq)]
588pub enum JustifySelfParseError<'a> {
589 InvalidValue(&'a str),
590}
591
592#[cfg(feature = "parser")]
593#[derive(Debug, Clone, PartialEq)]
594pub enum JustifySelfParseErrorOwned {
595 InvalidValue(alloc::string::String),
596}
597
598#[cfg(feature = "parser")]
599impl<'a> JustifySelfParseError<'a> {
600 pub fn to_contained(&self) -> JustifySelfParseErrorOwned {
601 match self {
602 JustifySelfParseError::InvalidValue(s) => {
603 JustifySelfParseErrorOwned::InvalidValue(s.to_string())
604 }
605 }
606 }
607}
608
609#[cfg(feature = "parser")]
610impl JustifySelfParseErrorOwned {
611 pub fn to_shared<'a>(&'a self) -> JustifySelfParseError<'a> {
612 match self {
613 JustifySelfParseErrorOwned::InvalidValue(s) => {
614 JustifySelfParseError::InvalidValue(s.as_str())
615 }
616 }
617 }
618}
619
620#[cfg(feature = "parser")]
621impl_debug_as_display!(JustifySelfParseError<'a>);
622#[cfg(feature = "parser")]
623impl_display! { JustifySelfParseError<'a>, {
624 InvalidValue(e) => format!("Invalid justify-self value: \"{}\"", e),
625}}
626
627#[cfg(feature = "parser")]
628pub fn parse_layout_justify_self<'a>(
629 input: &'a str,
630) -> Result<LayoutJustifySelf, JustifySelfParseError<'a>> {
631 match input.trim() {
632 "auto" => Ok(LayoutJustifySelf::Auto),
633 "start" | "flex-start" => Ok(LayoutJustifySelf::Start),
634 "end" | "flex-end" => Ok(LayoutJustifySelf::End),
635 "center" => Ok(LayoutJustifySelf::Center),
636 "stretch" => Ok(LayoutJustifySelf::Stretch),
637 _ => Err(JustifySelfParseError::InvalidValue(input)),
638 }
639}
640
641#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
643#[repr(C)]
644pub enum LayoutJustifyItems {
645 Start,
646 End,
647 Center,
648 Stretch,
649}
650
651impl Default for LayoutJustifyItems {
652 fn default() -> Self {
653 Self::Stretch
654 }
655}
656
657impl crate::props::formatter::PrintAsCssValue for LayoutJustifyItems {
658 fn print_as_css_value(&self) -> alloc::string::String {
659 match self {
660 LayoutJustifyItems::Start => "start".to_string(),
661 LayoutJustifyItems::End => "end".to_string(),
662 LayoutJustifyItems::Center => "center".to_string(),
663 LayoutJustifyItems::Stretch => "stretch".to_string(),
664 }
665 }
666}
667
668#[cfg(feature = "parser")]
669#[derive(Clone, PartialEq)]
670pub enum JustifyItemsParseError<'a> {
671 InvalidValue(&'a str),
672}
673
674#[cfg(feature = "parser")]
675#[derive(Debug, Clone, PartialEq)]
676pub enum JustifyItemsParseErrorOwned {
677 InvalidValue(alloc::string::String),
678}
679
680#[cfg(feature = "parser")]
681impl<'a> JustifyItemsParseError<'a> {
682 pub fn to_contained(&self) -> JustifyItemsParseErrorOwned {
683 match self {
684 JustifyItemsParseError::InvalidValue(s) => {
685 JustifyItemsParseErrorOwned::InvalidValue(s.to_string())
686 }
687 }
688 }
689}
690
691#[cfg(feature = "parser")]
692impl JustifyItemsParseErrorOwned {
693 pub fn to_shared<'a>(&'a self) -> JustifyItemsParseError<'a> {
694 match self {
695 JustifyItemsParseErrorOwned::InvalidValue(s) => {
696 JustifyItemsParseError::InvalidValue(s.as_str())
697 }
698 }
699 }
700}
701
702#[cfg(feature = "parser")]
703impl_debug_as_display!(JustifyItemsParseError<'a>);
704#[cfg(feature = "parser")]
705impl_display! { JustifyItemsParseError<'a>, {
706 InvalidValue(e) => format!("Invalid justify-items value: \"{}\"", e),
707}}
708
709#[cfg(feature = "parser")]
710pub fn parse_layout_justify_items<'a>(
711 input: &'a str,
712) -> Result<LayoutJustifyItems, JustifyItemsParseError<'a>> {
713 match input.trim() {
714 "start" => Ok(LayoutJustifyItems::Start),
715 "end" => Ok(LayoutJustifyItems::End),
716 "center" => Ok(LayoutJustifyItems::Center),
717 "stretch" => Ok(LayoutJustifyItems::Stretch),
718 _ => Err(JustifyItemsParseError::InvalidValue(input)),
719 }
720}
721
722#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
725#[repr(C)]
726pub struct LayoutGap {
727 pub inner: crate::props::basic::pixel::PixelValue,
728}
729
730impl core::fmt::Debug for LayoutGap {
731 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
732 write!(f, "{}", self.inner)
733 }
734}
735
736impl crate::props::formatter::PrintAsCssValue for LayoutGap {
737 fn print_as_css_value(&self) -> alloc::string::String {
738 self.inner.print_as_css_value()
739 }
740}
741
742impl FormatAsRustCode for LayoutGridAutoFlow {
745 fn format_as_rust_code(&self, _tabs: usize) -> String {
746 format!(
747 "LayoutGridAutoFlow::{}",
748 match self {
749 LayoutGridAutoFlow::Row => "Row",
750 LayoutGridAutoFlow::Column => "Column",
751 LayoutGridAutoFlow::RowDense => "RowDense",
752 LayoutGridAutoFlow::ColumnDense => "ColumnDense",
753 }
754 )
755 }
756}
757
758impl FormatAsRustCode for LayoutJustifySelf {
759 fn format_as_rust_code(&self, _tabs: usize) -> String {
760 format!(
761 "LayoutJustifySelf::{}",
762 match self {
763 LayoutJustifySelf::Auto => "Auto",
764 LayoutJustifySelf::Start => "Start",
765 LayoutJustifySelf::End => "End",
766 LayoutJustifySelf::Center => "Center",
767 LayoutJustifySelf::Stretch => "Stretch",
768 }
769 )
770 }
771}
772
773impl FormatAsRustCode for LayoutJustifyItems {
774 fn format_as_rust_code(&self, _tabs: usize) -> String {
775 format!(
776 "LayoutJustifyItems::{}",
777 match self {
778 LayoutJustifyItems::Start => "Start",
779 LayoutJustifyItems::End => "End",
780 LayoutJustifyItems::Center => "Center",
781 LayoutJustifyItems::Stretch => "Stretch",
782 }
783 )
784 }
785}
786
787impl FormatAsRustCode for LayoutGap {
788 fn format_as_rust_code(&self, _tabs: usize) -> String {
789 format!("LayoutGap::Exact({})", self.inner)
792 }
793}
794
795impl FormatAsRustCode for GridTrackSizing {
796 fn format_as_rust_code(&self, tabs: usize) -> String {
797 use crate::format_rust_code::format_pixel_value;
798 match self {
799 GridTrackSizing::Fixed(pv) => {
800 format!("GridTrackSizing::Fixed({})", format_pixel_value(pv))
801 }
802 GridTrackSizing::Fr(f) => format!("GridTrackSizing::Fr({})", f),
803 GridTrackSizing::MinContent => "GridTrackSizing::MinContent".to_string(),
804 GridTrackSizing::MaxContent => "GridTrackSizing::MaxContent".to_string(),
805 GridTrackSizing::Auto => "GridTrackSizing::Auto".to_string(),
806 GridTrackSizing::MinMax(minmax) => {
807 format!(
808 "GridTrackSizing::MinMax(GridMinMax {{ min: Box::new({}), max: Box::new({}) }})",
809 minmax.min.format_as_rust_code(tabs),
810 minmax.max.format_as_rust_code(tabs)
811 )
812 }
813 GridTrackSizing::FitContent(pv) => {
814 format!("GridTrackSizing::FitContent({})", format_pixel_value(pv))
815 }
816 }
817 }
818}
819
820impl FormatAsRustCode for GridAutoTracks {
821 fn format_as_rust_code(&self, tabs: usize) -> String {
822 let tracks: Vec<String> = self
823 .tracks
824 .as_ref()
825 .iter()
826 .map(|t| t.format_as_rust_code(tabs))
827 .collect();
828 format!(
829 "GridAutoTracks {{ tracks: GridTrackSizingVec::from_vec(vec![{}]) }}",
830 tracks.join(", ")
831 )
832 }
833}
834
835#[cfg(feature = "parser")]
836pub fn parse_layout_gap<'a>(
837 input: &'a str,
838) -> Result<LayoutGap, crate::props::basic::pixel::CssPixelValueParseError<'a>> {
839 crate::props::basic::pixel::parse_pixel_value(input).map(|p| LayoutGap { inner: p })
840}
841
842#[cfg(feature = "parser")]
843fn parse_grid_line_owned(input: &str) -> Result<GridLine, ()> {
844 let input = input.trim();
845
846 if input == "auto" {
847 return Ok(GridLine::Auto);
848 }
849
850 if input.starts_with("span ") {
851 let num_str = &input[5..].trim();
852 if let Ok(num) = num_str.parse::<i32>() {
853 return Ok(GridLine::Span(num));
854 }
855 return Err(());
856 }
857
858 if let Ok(num) = input.parse::<i32>() {
860 return Ok(GridLine::Line(num));
861 }
862
863 Ok(GridLine::Named(NamedGridLine::create(
865 input.to_string().into(),
866 None,
867 )))
868}
869
870#[cfg(all(test, feature = "parser"))]
871mod tests {
872 use super::*;
873
874 #[test]
876 fn test_parse_grid_template_none() {
877 let result = parse_grid_template("none").unwrap();
878 assert_eq!(result.tracks.len(), 0);
879 }
880
881 #[test]
882 fn test_parse_grid_template_single_px() {
883 let result = parse_grid_template("100px").unwrap();
884 assert_eq!(result.tracks.len(), 1);
885 assert!(matches!(
886 result.tracks.as_ref()[0],
887 GridTrackSizing::Fixed(_)
888 ));
889 }
890
891 #[test]
892 fn test_parse_grid_template_multiple_tracks() {
893 let result = parse_grid_template("100px 200px 1fr").unwrap();
894 assert_eq!(result.tracks.len(), 3);
895 }
896
897 #[test]
898 fn test_parse_grid_template_fr_units() {
899 let result = parse_grid_template("1fr 2fr 1fr").unwrap();
900 assert_eq!(result.tracks.len(), 3);
901 assert!(matches!(
902 result.tracks.as_ref()[0],
903 GridTrackSizing::Fr(100)
904 ));
905 assert!(matches!(
906 result.tracks.as_ref()[1],
907 GridTrackSizing::Fr(200)
908 ));
909 }
910
911 #[test]
912 fn test_parse_grid_template_fractional_fr() {
913 let result = parse_grid_template("0.5fr 1.5fr").unwrap();
914 assert_eq!(result.tracks.len(), 2);
915 assert!(matches!(result.tracks.as_ref()[0], GridTrackSizing::Fr(50)));
916 assert!(matches!(
917 result.tracks.as_ref()[1],
918 GridTrackSizing::Fr(150)
919 ));
920 }
921
922 #[test]
923 fn test_parse_grid_template_auto() {
924 let result = parse_grid_template("auto 100px auto").unwrap();
925 assert_eq!(result.tracks.len(), 3);
926 assert!(matches!(result.tracks.as_ref()[0], GridTrackSizing::Auto));
927 assert!(matches!(result.tracks.as_ref()[2], GridTrackSizing::Auto));
928 }
929
930 #[test]
931 fn test_parse_grid_template_min_max_content() {
932 let result = parse_grid_template("min-content max-content auto").unwrap();
933 assert_eq!(result.tracks.len(), 3);
934 assert!(matches!(
935 result.tracks.as_ref()[0],
936 GridTrackSizing::MinContent
937 ));
938 assert!(matches!(
939 result.tracks.as_ref()[1],
940 GridTrackSizing::MaxContent
941 ));
942 }
943
944 #[test]
945 fn test_parse_grid_template_minmax() {
946 let result = parse_grid_template("minmax(100px, 1fr)").unwrap();
947 assert_eq!(result.tracks.len(), 1);
948 assert!(matches!(
949 result.tracks.as_ref()[0],
950 GridTrackSizing::MinMax(_)
951 ));
952 }
953
954 #[test]
955 fn test_parse_grid_template_minmax_complex() {
956 let result = parse_grid_template("minmax(min-content, max-content)").unwrap();
957 assert_eq!(result.tracks.len(), 1);
958 }
959
960 #[test]
961 fn test_parse_grid_template_fit_content() {
962 let result = parse_grid_template("fit-content(200px)").unwrap();
963 assert_eq!(result.tracks.len(), 1);
964 assert!(matches!(
965 result.tracks.as_ref()[0],
966 GridTrackSizing::FitContent(_)
967 ));
968 }
969
970 #[test]
971 fn test_parse_grid_template_mixed() {
972 let result = parse_grid_template("100px minmax(100px, 1fr) auto 2fr").unwrap();
973 assert_eq!(result.tracks.len(), 4);
974 }
975
976 #[test]
977 fn test_parse_grid_template_percent() {
978 let result = parse_grid_template("25% 50% 25%").unwrap();
979 assert_eq!(result.tracks.len(), 3);
980 }
981
982 #[test]
983 fn test_parse_grid_template_em_units() {
984 let result = parse_grid_template("10em 20em 1fr").unwrap();
985 assert_eq!(result.tracks.len(), 3);
986 }
987
988 #[test]
990 fn test_parse_grid_placement_auto() {
991 let result = parse_grid_placement("auto").unwrap();
992 assert!(matches!(result.grid_start, GridLine::Auto));
993 assert!(matches!(result.grid_end, GridLine::Auto));
994 }
995
996 #[test]
997 fn test_parse_grid_placement_line_number() {
998 let result = parse_grid_placement("1").unwrap();
999 assert!(matches!(result.grid_start, GridLine::Line(1)));
1000 assert!(matches!(result.grid_end, GridLine::Auto));
1001 }
1002
1003 #[test]
1004 fn test_parse_grid_placement_negative_line() {
1005 let result = parse_grid_placement("-1").unwrap();
1006 assert!(matches!(result.grid_start, GridLine::Line(-1)));
1007 }
1008
1009 #[test]
1010 fn test_parse_grid_placement_span() {
1011 let result = parse_grid_placement("span 2").unwrap();
1012 assert!(matches!(result.grid_start, GridLine::Span(2)));
1013 }
1014
1015 #[test]
1016 fn test_parse_grid_placement_start_end() {
1017 let result = parse_grid_placement("1 / 3").unwrap();
1018 assert!(matches!(result.grid_start, GridLine::Line(1)));
1019 assert!(matches!(result.grid_end, GridLine::Line(3)));
1020 }
1021
1022 #[test]
1023 fn test_parse_grid_placement_span_end() {
1024 let result = parse_grid_placement("1 / span 2").unwrap();
1025 assert!(matches!(result.grid_start, GridLine::Line(1)));
1026 assert!(matches!(result.grid_end, GridLine::Span(2)));
1027 }
1028
1029 #[test]
1030 fn test_parse_grid_placement_named_line() {
1031 let result = parse_grid_placement("header-start").unwrap();
1032 assert!(matches!(result.grid_start, GridLine::Named(_)));
1033 }
1034
1035 #[test]
1036 fn test_parse_grid_placement_named_start_end() {
1037 let result = parse_grid_placement("header-start / header-end").unwrap();
1038 assert!(matches!(result.grid_start, GridLine::Named(_)));
1039 assert!(matches!(result.grid_end, GridLine::Named(_)));
1040 }
1041
1042 #[test]
1044 fn test_parse_grid_template_whitespace() {
1045 let result = parse_grid_template(" 100px 200px ").unwrap();
1046 assert_eq!(result.tracks.len(), 2);
1047 }
1048
1049 #[test]
1050 fn test_parse_grid_placement_whitespace() {
1051 let result = parse_grid_placement(" 1 / 3 ").unwrap();
1052 assert!(matches!(result.grid_start, GridLine::Line(1)));
1053 assert!(matches!(result.grid_end, GridLine::Line(3)));
1054 }
1055
1056 #[test]
1057 fn test_parse_grid_template_zero_fr() {
1058 let result = parse_grid_template("0fr").unwrap();
1059 assert!(matches!(result.tracks.as_ref()[0], GridTrackSizing::Fr(0)));
1060 }
1061
1062 #[test]
1063 fn test_parse_grid_placement_zero_line() {
1064 let result = parse_grid_placement("0").unwrap();
1065 assert!(matches!(result.grid_start, GridLine::Line(0)));
1066 }
1067}