Skip to main content

azul_css/
format_rust_code.rs

1//! Module for converting CSS types to Rust code
2
3#[cfg(not(feature = "std"))]
4use alloc::string::ToString;
5use alloc::{collections::btree_map::BTreeMap, format, string::String, vec::Vec};
6use core::hash::Hash;
7
8use crate::{
9    corety::*,
10    css::*,
11    props::{basic::*, layout::*, property::*, style::*},
12};
13
14// Trait for formatting types as Rust code
15pub trait FormatAsRustCode {
16    fn format_as_rust_code(&self, tabs: usize) -> String;
17}
18
19// Trait for getting hash of types
20pub trait GetHash {
21    fn get_hash(&self) -> u64;
22}
23
24impl<T: Hash> GetHash for T {
25    fn get_hash(&self) -> u64 {
26        use highway::{HighwayHash, HighwayHasher, Key};
27        let mut hasher = HighwayHasher::new(Key([0; 4]));
28        self.hash(&mut hasher);
29        hasher.finalize64()
30    }
31}
32
33// In order to generate the Rust code, all items that implement Drop
34// have to be declared before being used.
35#[derive(Default)]
36pub struct VecContents {
37    // the u64 is the hash of the type (generated by string.get_hash())
38    pub strings: BTreeMap<u64, AzString>,
39    pub style_filters: BTreeMap<u64, StyleFilterVec>,
40    pub style_background_sizes: BTreeMap<u64, StyleBackgroundSizeVec>,
41    pub style_background_repeats: BTreeMap<u64, StyleBackgroundRepeatVec>,
42    pub style_background_contents: BTreeMap<u64, StyleBackgroundContentVec>,
43    pub style_background_positions: BTreeMap<u64, StyleBackgroundPositionVec>,
44    pub style_transforms: BTreeMap<u64, StyleTransformVec>,
45    pub font_families: BTreeMap<u64, StyleFontFamilyVec>,
46    pub linear_color_stops: BTreeMap<u64, NormalizedLinearColorStopVec>,
47    pub radial_color_stops: BTreeMap<u64, NormalizedRadialColorStopVec>,
48}
49
50impl VecContents {
51    pub fn format(&self, tabs: usize) -> String {
52        let mut result = String::new();
53        let t = "    ".repeat(tabs);
54        let t2 = "    ".repeat(tabs + 1);
55
56        for (key, item) in self.strings.iter() {
57            result.push_str(&format!(
58                "\r\n    const STRING_{}: AzString = AzString::from_const_str(\"{}\");",
59                key,
60                item.as_str()
61            ));
62        }
63
64        for (key, item) in self.style_filters.iter() {
65            let val = item
66                .iter()
67                .map(|filter| format_style_filter(filter, tabs + 1))
68                .collect::<Vec<_>>()
69                .join(&format!(",\r\n{}", t));
70
71            result.push_str(&format!(
72                "\r\n    const STYLE_FILTER_{}_ITEMS: &[StyleFilter] = &[\r\n{}{}\r\n{}];",
73                key, t2, val, t
74            ));
75        }
76
77        for (key, item) in self.style_background_sizes.iter() {
78            let val = item
79                .iter()
80                .map(|bgs| format_style_background_size(bgs))
81                .collect::<Vec<_>>()
82                .join(&format!(",\r\n{}", t));
83
84            result.push_str(&format!(
85                "\r\n    const STYLE_BACKGROUND_SIZE_{}_ITEMS: &[StyleBackgroundSize] = \
86                 &[\r\n{}{}\r\n{}];",
87                key, t2, val, t
88            ));
89        }
90
91        for (key, item) in self.style_background_repeats.iter() {
92            let val = item
93                .iter()
94                .map(|bgr| bgr.format_as_rust_code(tabs + 1))
95                .collect::<Vec<_>>()
96                .join(&format!(",\r\n{}", t));
97
98            result.push_str(&format!(
99                "\r\n    const STYLE_BACKGROUND_REPEAT_{}_ITEMS: &[StyleBackgroundRepeat] = \
100                 &[\r\n{}{}\r\n{}];",
101                key, t2, val, t
102            ));
103        }
104
105        for (key, item) in self.style_background_contents.iter() {
106            let val = item
107                .iter()
108                .map(|bgc| format_style_background_content(bgc, tabs + 1))
109                .collect::<Vec<_>>()
110                .join(&format!(",\r\n{}", t));
111
112            result.push_str(&format!(
113                "\r\n    const STYLE_BACKGROUND_CONTENT_{}_ITEMS: &[StyleBackgroundContent] = \
114                 &[\r\n{}{}\r\n{}];",
115                key, t2, val, t
116            ));
117        }
118
119        for (key, item) in self.style_background_positions.iter() {
120            let val = item
121                .iter()
122                .map(|bgp| format_style_background_position(bgp, tabs))
123                .collect::<Vec<_>>()
124                .join(&format!(",\r\n{}", t));
125
126            result.push_str(&format!(
127                "\r\n    const STYLE_BACKGROUND_POSITION_{}_ITEMS: &[StyleBackgroundPosition] = \
128                 &[\r\n{}{}\r\n{}];",
129                key, t2, val, t
130            ));
131        }
132
133        for (key, item) in self.style_transforms.iter() {
134            let val = format_style_transforms(item.as_ref(), tabs + 1);
135
136            result.push_str(&format!(
137                "\r\n    const STYLE_TRANSFORM_{}_ITEMS: &[StyleTransform] = &[\r\n{}{}\r\n{}];",
138                key, t2, val, t
139            ));
140        }
141
142        for (key, item) in self.font_families.iter() {
143            let val = format_font_ids(item.as_ref(), tabs + 1);
144
145            result.push_str(&format!(
146                "\r\n    const STYLE_FONT_FAMILY_{}_ITEMS: &[StyleFontFamily] = &[\r\n{}{}\r\n{}];",
147                key, t2, val, t
148            ));
149        }
150
151        for (key, item) in self.linear_color_stops.iter() {
152            let val = format_linear_color_stops(item.as_ref(), 1);
153
154            result.push_str(&format!(
155                "\r\n    const LINEAR_COLOR_STOP_{}_ITEMS: &[NormalizedLinearColorStop] = \
156                 &[\r\n{}{}\r\n{}];",
157                key, t2, val, t
158            ));
159        }
160
161        for (key, item) in self.radial_color_stops.iter() {
162            let val = format_radial_color_stops(item.as_ref(), tabs);
163
164            result.push_str(&format!(
165                "\r\n    const RADIAL_COLOR_STOP_{}_ITEMS: &[NormalizedRadialColorStop] = \
166                 &[\r\n{}{}\r\n{}];",
167                key, t2, val, t
168            ));
169        }
170
171        result
172    }
173
174    // given a CSS property, clones all the necessary strings (see class documentation)
175    pub fn insert_from_css_property(&mut self, prop: &CssProperty) {
176        match prop {
177            CssProperty::FontFamily(CssPropertyValue::Exact(v)) => {
178                for family in v.iter() {
179                    match family {
180                        StyleFontFamily::System(s) => {
181                            // if the font-family is surrounded by quotes, strip them ("Arial" ->
182                            // Arial)
183                            let s = s.as_str();
184                            let s = s.trim();
185                            let s = s.trim_start_matches('\"');
186                            let s = s.trim_end_matches('\"');
187                            let s = s.trim_start_matches('\'');
188                            let s = s.trim_end_matches('\'');
189
190                            self.strings.insert(s.get_hash(), s.to_string().into());
191                        }
192                        StyleFontFamily::File(s) => {
193                            let s = s.as_str();
194                            let s = s.trim();
195                            let s = s.trim_start_matches('\"');
196                            let s = s.trim_end_matches('\"');
197                            let s = s.trim_start_matches('\'');
198                            let s = s.trim_end_matches('\'');
199
200                            self.strings.insert(s.get_hash(), s.to_string().into());
201                        }
202                        _ => {}
203                    }
204                }
205                self.font_families.insert(v.get_hash(), v.clone());
206            }
207            CssProperty::Transform(CssPropertyValue::Exact(v)) => {
208                self.style_transforms.insert(v.get_hash(), v.clone());
209            }
210            CssProperty::BackgroundRepeat(CssPropertyValue::Exact(v)) => {
211                self.style_background_repeats
212                    .insert(v.get_hash(), v.clone());
213            }
214            CssProperty::BackgroundSize(CssPropertyValue::Exact(v)) => {
215                self.style_background_sizes.insert(v.get_hash(), v.clone());
216            }
217            CssProperty::BackgroundPosition(CssPropertyValue::Exact(v)) => {
218                self.style_background_positions
219                    .insert(v.get_hash(), v.clone());
220            }
221            CssProperty::BackgroundContent(CssPropertyValue::Exact(v)) => {
222                for background in v.iter() {
223                    match background {
224                        StyleBackgroundContent::Image(id) => {
225                            self.strings.insert(id.get_hash(), id.clone());
226                        }
227                        StyleBackgroundContent::LinearGradient(lg) => {
228                            self.linear_color_stops
229                                .insert(lg.stops.get_hash(), lg.stops.clone());
230                        }
231                        StyleBackgroundContent::RadialGradient(rg) => {
232                            self.linear_color_stops
233                                .insert(rg.stops.get_hash(), rg.stops.clone());
234                        }
235                        StyleBackgroundContent::ConicGradient(lg) => {
236                            self.radial_color_stops
237                                .insert(lg.stops.get_hash(), lg.stops.clone());
238                        }
239                        _ => {}
240                    }
241                }
242                self.style_background_contents
243                    .insert(v.get_hash(), v.clone());
244            }
245            CssProperty::Filter(CssPropertyValue::Exact(v)) => {
246                self.style_filters.insert(v.get_hash(), v.clone());
247            }
248            CssProperty::BackdropFilter(CssPropertyValue::Exact(v)) => {
249                self.style_filters.insert(v.get_hash(), v.clone());
250            }
251            _ => {}
252        }
253    }
254}
255
256// Helper functions for formatting values
257
258pub fn format_pixel_value(p: &PixelValue) -> String {
259    let value = p.number.get();
260
261    // Extract integer and fractional parts
262    // For example: 1.5 -> pre_comma=1, post_comma=50
263    //              0.83 -> pre_comma=0, post_comma=83
264    //              2.0 -> pre_comma=2, post_comma=0
265    let pre_comma = libm::floorf(value) as isize;
266    let fraction = value - pre_comma as f32;
267    // Multiply by 100 to get two decimal places, then round
268    let post_comma = libm::roundf(fraction * 100.0) as isize;
269
270    match p.metric {
271        SizeMetric::Px => format!(
272            "PixelValue::const_from_metric_fractional(SizeMetric::Px, {}, {})",
273            pre_comma, post_comma
274        ),
275        SizeMetric::Pt => format!(
276            "PixelValue::const_pt_fractional({}, {})",
277            pre_comma, post_comma
278        ),
279        SizeMetric::Em => format!(
280            "PixelValue::const_em_fractional({}, {})",
281            pre_comma, post_comma
282        ),
283        SizeMetric::Rem => format!(
284            "PixelValue::const_from_metric_fractional(SizeMetric::Rem, {}, {})",
285            pre_comma, post_comma
286        ),
287        SizeMetric::Percent => format!(
288            "PixelValue::const_from_metric_fractional(SizeMetric::Percent, {}, {})",
289            pre_comma, post_comma
290        ),
291        SizeMetric::In => format!(
292            "PixelValue::const_from_metric_fractional(SizeMetric::In, {}, {})",
293            pre_comma, post_comma
294        ),
295        SizeMetric::Cm => format!(
296            "PixelValue::const_from_metric_fractional(SizeMetric::Cm, {}, {})",
297            pre_comma, post_comma
298        ),
299        SizeMetric::Mm => format!(
300            "PixelValue::const_from_metric_fractional(SizeMetric::Mm, {}, {})",
301            pre_comma, post_comma
302        ),
303        SizeMetric::Vw => format!(
304            "PixelValue::const_from_metric_fractional(SizeMetric::Vw, {}, {})",
305            pre_comma, post_comma
306        ),
307        SizeMetric::Vh => format!(
308            "PixelValue::const_from_metric_fractional(SizeMetric::Vh, {}, {})",
309            pre_comma, post_comma
310        ),
311        SizeMetric::Vmin => format!(
312            "PixelValue::const_from_metric_fractional(SizeMetric::Vmin, {}, {})",
313            pre_comma, post_comma
314        ),
315        SizeMetric::Vmax => format!(
316            "PixelValue::const_from_metric_fractional(SizeMetric::Vmax, {}, {})",
317            pre_comma, post_comma
318        ),
319    }
320}
321
322pub fn format_pixel_value_no_percent(p: &PixelValueNoPercent) -> String {
323    format!(
324        "PixelValueNoPercent {{ inner: {} }}",
325        format_pixel_value(&p.inner)
326    )
327}
328
329pub fn format_float_value(f: &FloatValue) -> String {
330    format!("FloatValue::const_new({})", libm::roundf(f.get()) as isize)
331}
332
333pub fn format_percentage_value(f: &PercentageValue) -> String {
334    format!(
335        "PercentageValue::const_new({})",
336        libm::roundf(f.normalized() * 100.0) as isize
337    )
338}
339
340pub fn format_angle_value(f: &AngleValue) -> String {
341    format!(
342        "AngleValue::const_deg({})",
343        libm::roundf(f.to_degrees()) as isize
344    )
345}
346
347pub fn format_color_value(c: &ColorU) -> String {
348    format!(
349        "ColorU {{ r: {}, g: {}, b: {}, a: {} }}",
350        c.r, c.g, c.b, c.a
351    )
352}
353
354pub fn format_color_or_system(c: &crate::props::basic::color::ColorOrSystem) -> String {
355    use crate::props::basic::color::{ColorOrSystem, SystemColorRef};
356    match c {
357        ColorOrSystem::Color(color) => format!("ColorOrSystem::Color({})", format_color_value(color)),
358        ColorOrSystem::System(system_ref) => {
359            let variant = match system_ref {
360                SystemColorRef::Text => "Text",
361                SystemColorRef::Background => "Background",
362                SystemColorRef::Accent => "Accent",
363                SystemColorRef::AccentText => "AccentText",
364                SystemColorRef::ButtonFace => "ButtonFace",
365                SystemColorRef::ButtonText => "ButtonText",
366                SystemColorRef::WindowBackground => "WindowBackground",
367                SystemColorRef::SelectionBackground => "SelectionBackground",
368                SystemColorRef::SelectionText => "SelectionText",
369            };
370            format!("ColorOrSystem::System(SystemColorRef::{})", variant)
371        }
372    }
373}
374
375// Macro implementations for common patterns
376
377macro_rules! impl_float_value_fmt {
378    ($struct_name:ident) => {
379        impl FormatAsRustCode for $struct_name {
380            fn format_as_rust_code(&self, _tabs: usize) -> String {
381                format!(
382                    "{} {{ inner: {} }}",
383                    stringify!($struct_name),
384                    format_float_value(&self.inner)
385                )
386            }
387        }
388    };
389}
390
391impl_float_value_fmt!(LayoutFlexGrow);
392impl_float_value_fmt!(LayoutFlexShrink);
393
394macro_rules! impl_percentage_value_fmt {
395    ($struct_name:ident) => {
396        impl FormatAsRustCode for $struct_name {
397            fn format_as_rust_code(&self, _tabs: usize) -> String {
398                format!(
399                    "{} {{ inner: {} }}",
400                    stringify!($struct_name),
401                    format_percentage_value(&self.inner)
402                )
403            }
404        }
405    };
406}
407
408impl_percentage_value_fmt!(StyleLineHeight);
409impl_percentage_value_fmt!(StyleOpacity);
410
411macro_rules! impl_pixel_value_fmt {
412    ($struct_name:ident) => {
413        impl FormatAsRustCode for $struct_name {
414            fn format_as_rust_code(&self, _tabs: usize) -> String {
415                format!(
416                    "{} {{ inner: {} }}",
417                    stringify!($struct_name),
418                    format_pixel_value(&self.inner)
419                )
420            }
421        }
422    };
423}
424
425impl_pixel_value_fmt!(StyleTabSize);
426impl_pixel_value_fmt!(StyleBorderTopLeftRadius);
427impl_pixel_value_fmt!(StyleBorderBottomLeftRadius);
428impl_pixel_value_fmt!(StyleBorderTopRightRadius);
429impl_pixel_value_fmt!(StyleBorderBottomRightRadius);
430
431impl_pixel_value_fmt!(LayoutBorderTopWidth);
432impl_pixel_value_fmt!(LayoutBorderLeftWidth);
433impl_pixel_value_fmt!(LayoutBorderRightWidth);
434impl_pixel_value_fmt!(LayoutBorderBottomWidth);
435impl_pixel_value_fmt!(StyleLetterSpacing);
436impl_pixel_value_fmt!(StyleWordSpacing);
437impl_pixel_value_fmt!(StyleFontSize);
438
439impl_pixel_value_fmt!(LayoutMarginTop);
440impl_pixel_value_fmt!(LayoutMarginBottom);
441impl_pixel_value_fmt!(LayoutMarginRight);
442impl_pixel_value_fmt!(LayoutMarginLeft);
443
444impl_pixel_value_fmt!(LayoutPaddingTop);
445impl_pixel_value_fmt!(LayoutPaddingBottom);
446impl_pixel_value_fmt!(LayoutPaddingRight);
447impl_pixel_value_fmt!(LayoutPaddingLeft);
448impl_pixel_value_fmt!(LayoutPaddingInlineStart);
449impl_pixel_value_fmt!(LayoutPaddingInlineEnd);
450
451impl FormatAsRustCode for LayoutWidth {
452    fn format_as_rust_code(&self, _tabs: usize) -> String {
453        match self {
454            LayoutWidth::Auto => "LayoutWidth::Auto".to_string(),
455            LayoutWidth::Px(px) => format!("LayoutWidth::Px({})", format_pixel_value(px)),
456            LayoutWidth::MinContent => "LayoutWidth::MinContent".to_string(),
457            LayoutWidth::MaxContent => "LayoutWidth::MaxContent".to_string(),
458            LayoutWidth::Calc(items) => format!("LayoutWidth::Calc(/* {} items */)", items.len()),
459        }
460    }
461}
462
463impl FormatAsRustCode for LayoutHeight {
464    fn format_as_rust_code(&self, _tabs: usize) -> String {
465        match self {
466            LayoutHeight::Auto => "LayoutHeight::Auto".to_string(),
467            LayoutHeight::Px(px) => format!("LayoutHeight::Px({})", format_pixel_value(px)),
468            LayoutHeight::MinContent => "LayoutHeight::MinContent".to_string(),
469            LayoutHeight::MaxContent => "LayoutHeight::MaxContent".to_string(),
470            LayoutHeight::Calc(items) => format!("LayoutHeight::Calc(/* {} items */)", items.len()),
471        }
472    }
473}
474
475impl_pixel_value_fmt!(LayoutMinHeight);
476impl_pixel_value_fmt!(LayoutMinWidth);
477impl_pixel_value_fmt!(LayoutMaxWidth);
478impl_pixel_value_fmt!(LayoutMaxHeight);
479impl_pixel_value_fmt!(LayoutTop);
480impl_pixel_value_fmt!(LayoutInsetBottom);
481
482impl_pixel_value_fmt!(LayoutRight);
483impl_pixel_value_fmt!(LayoutLeft);
484
485// LayoutFlexBasis implementation moved to `props/layout/flex.rs`.
486
487impl_pixel_value_fmt!(LayoutColumnGap);
488impl_pixel_value_fmt!(LayoutRowGap);
489
490impl_grid_value_fmt!(GridTemplate);
491impl_grid_value_fmt!(GridPlacement);
492
493impl_color_value_fmt!(StyleTextColor);
494impl_color_value_fmt!(StyleBorderTopColor);
495impl_color_value_fmt!(StyleBorderLeftColor);
496impl_color_value_fmt!(StyleBorderRightColor);
497impl_color_value_fmt!(StyleBorderBottomColor);
498
499impl_enum_fmt!(
500    StyleMixBlendMode,
501    Normal,
502    Multiply,
503    Screen,
504    Overlay,
505    Darken,
506    Lighten,
507    ColorDodge,
508    ColorBurn,
509    HardLight,
510    SoftLight,
511    Difference,
512    Exclusion,
513    Hue,
514    Saturation,
515    Color,
516    Luminosity
517);
518
519impl_enum_fmt!(StyleHyphens, Auto, None);
520
521impl_enum_fmt!(StyleDirection, Ltr, Rtl);
522
523impl_enum_fmt!(StyleWhiteSpace, Normal, Pre, Nowrap, PreWrap, PreLine, BreakSpaces);
524
525impl_enum_fmt!(StyleVisibility, Visible, Hidden, Collapse);
526
527impl_enum_fmt!(LayoutWritingMode, HorizontalTb, VerticalRl, VerticalLr);
528
529impl_enum_fmt!(LayoutClear, None, Left, Right, Both);
530
531impl_enum_fmt!(
532    StyleCursor,
533    Alias,
534    AllScroll,
535    Cell,
536    ColResize,
537    ContextMenu,
538    Copy,
539    Crosshair,
540    Default,
541    EResize,
542    EwResize,
543    Grab,
544    Grabbing,
545    Help,
546    Move,
547    NResize,
548    NsResize,
549    NeswResize,
550    NwseResize,
551    Pointer,
552    Progress,
553    RowResize,
554    SResize,
555    SeResize,
556    Text,
557    Unset,
558    VerticalText,
559    WResize,
560    Wait,
561    ZoomIn,
562    ZoomOut
563);
564
565impl_enum_fmt!(
566    BorderStyle,
567    None,
568    Solid,
569    Double,
570    Dotted,
571    Dashed,
572    Hidden,
573    Groove,
574    Ridge,
575    Inset,
576    Outset
577);
578
579impl_enum_fmt!(
580    StyleBackgroundRepeat,
581    NoRepeat,
582    PatternRepeat,
583    RepeatX,
584    RepeatY
585);
586
587impl_enum_fmt!(
588    LayoutDisplay,
589    None,
590    Block,
591    Inline,
592    InlineBlock,
593    Flex,
594    InlineFlex,
595    Table,
596    InlineTable,
597    TableRowGroup,
598    TableHeaderGroup,
599    TableFooterGroup,
600    TableRow,
601    TableColumnGroup,
602    TableColumn,
603    TableCell,
604    TableCaption,
605    ListItem,
606    RunIn,
607    Marker,
608    Grid,
609    InlineGrid,
610    FlowRoot
611);
612
613impl_enum_fmt!(LayoutFloat, Left, Right, None);
614
615impl_enum_fmt!(LayoutBoxSizing, ContentBox, BorderBox);
616
617impl_enum_fmt!(LayoutFlexDirection, Row, RowReverse, Column, ColumnReverse);
618
619impl_enum_fmt!(LayoutFlexWrap, Wrap, NoWrap, WrapReverse);
620
621impl_enum_fmt!(
622    LayoutJustifyContent,
623    Start,
624    End,
625    FlexStart,
626    FlexEnd,
627    Center,
628    SpaceBetween,
629    SpaceAround,
630    SpaceEvenly
631);
632
633impl_enum_fmt!(LayoutAlignItems, Stretch, Center, Start, End, Baseline);
634
635impl_enum_fmt!(
636    LayoutAlignContent,
637    Start,
638    End,
639    Stretch,
640    Center,
641    SpaceBetween,
642    SpaceAround
643);
644
645impl_enum_fmt!(Shape, Circle, Ellipse);
646
647impl_enum_fmt!(LayoutOverflow, Auto, Scroll, Visible, Hidden, Clip);
648
649impl_enum_fmt!(StyleTextAlign, Center, Left, Right, Justify, Start, End);
650
651impl_enum_fmt!(StyleUserSelect, Auto, Text, None, All);
652
653impl_enum_fmt!(StyleTextDecoration, None, Underline, Overline, LineThrough);
654
655impl_enum_fmt!(
656    DirectionCorner,
657    Right,
658    Left,
659    Top,
660    Bottom,
661    TopRight,
662    TopLeft,
663    BottomRight,
664    BottomLeft
665);
666
667impl_enum_fmt!(ExtendMode, Clamp, Repeat);
668
669impl_enum_fmt!(StyleBackfaceVisibility, Visible, Hidden);
670
671// Complex type implementations
672
673fn format_style_background_size(c: &StyleBackgroundSize) -> String {
674    match c {
675        StyleBackgroundSize::Contain => String::from("StyleBackgroundSize::Contain"),
676        StyleBackgroundSize::Cover => String::from("StyleBackgroundSize::Cover"),
677        StyleBackgroundSize::ExactSize(size) => format!(
678            "StyleBackgroundSize::ExactSize(PixelValueSize {{ width: {}, height: {} }})",
679            format_pixel_value(&size.width),
680            format_pixel_value(&size.height)
681        ),
682    }
683}
684
685// Scrollbar-related impls moved to `props/style/scrollbar.rs`
686
687pub fn format_scrollbar_info(s: &ScrollbarInfo, tabs: usize) -> String {
688    let t = String::from("    ").repeat(tabs);
689    let t1 = String::from("    ").repeat(tabs + 1);
690    format!(
691        "ScrollbarInfo {{\r\n{}width: {},\r\n{}padding_left: {},\r\n{}padding_right: \
692         {},\r\n{}track: {},\r\n{}thumb: {},\r\n{}button: {},\r\n{}button: {},\r\n{}resizer: \
693         {},\r\n{}}}",
694        t1,
695        s.width.format_as_rust_code(tabs + 1),
696        t1,
697        s.padding_left.format_as_rust_code(tabs + 1),
698        t1,
699        s.padding_right.format_as_rust_code(tabs + 1),
700        t1,
701        format_style_background_content(&s.track, tabs + 1),
702        t1,
703        format_style_background_content(&s.thumb, tabs + 1),
704        t1,
705        format_style_background_content(&s.button, tabs + 1),
706        t1,
707        format_style_background_content(&s.corner, tabs + 1),
708        t1,
709        format_style_background_content(&s.resizer, tabs + 1),
710        t
711    )
712}
713
714// Background/filter vec impls moved to their respective modules.
715
716fn format_style_background_content(content: &StyleBackgroundContent, tabs: usize) -> String {
717    match content {
718        StyleBackgroundContent::LinearGradient(l) => format!(
719            "StyleBackgroundContent::LinearGradient({})",
720            format_linear_gradient(l, tabs)
721        ),
722        StyleBackgroundContent::RadialGradient(r) => format!(
723            "StyleBackgroundContent::RadialGradient({})",
724            format_radial_gradient(r, tabs)
725        ),
726        StyleBackgroundContent::ConicGradient(r) => format!(
727            "StyleBackgroundContent::ConicGradient({})",
728            format_conic_gradient(r, tabs)
729        ),
730        StyleBackgroundContent::Image(id) => format!("StyleBackgroundContent::Image({:?})", id),
731        StyleBackgroundContent::Color(c) => {
732            format!("StyleBackgroundContent::Color({})", format_color_value(c))
733        }
734    }
735}
736
737fn format_direction(d: &Direction, tabs: usize) -> String {
738    match d {
739        Direction::Angle(fv) => format!("Direction::Angle({})", format_angle_value(fv)),
740        Direction::FromTo(DirectionCorners { dir_from, dir_to }) => format!(
741            "Direction::FromTo(DirectionCorners {{ dir_from: {}, dir_to: {} }})",
742            dir_from.format_as_rust_code(tabs + 1),
743            dir_to.format_as_rust_code(tabs + 1)
744        ),
745    }
746}
747
748fn format_linear_gradient(l: &LinearGradient, tabs: usize) -> String {
749    let t = String::from("    ").repeat(tabs);
750    let t1 = String::from("    ").repeat(tabs + 1);
751    format!(
752        "LinearGradient {{\r\n{}direction: {},\r\n{}extend_mode: {},\r\n{}stops: \
753         NormalizedLinearColorStopVec::from_const_slice(LINEAR_COLOR_STOP_{}_ITEMS),\r\n{}}}",
754        t1,
755        format_direction(&l.direction, tabs + 1),
756        t1,
757        l.extend_mode.format_as_rust_code(tabs + 1),
758        t1,
759        l.stops.get_hash(),
760        t,
761    )
762}
763
764fn format_conic_gradient(r: &ConicGradient, tabs: usize) -> String {
765    let t = String::from("    ").repeat(tabs);
766    let t1 = String::from("    ").repeat(tabs + 1);
767
768    format!(
769        "ConicGradient {{\r\n{}extend_mode: {},\r\n{}center: {},\r\n{}angle: {},\r\n{}stops: \
770         NormalizedRadialColorStopVec::from_const_slice(RADIAL_COLOR_STOP_{}_ITEMS),\r\n{}}}",
771        t1,
772        r.extend_mode.format_as_rust_code(tabs + 1),
773        t1,
774        format_style_background_position(&r.center, tabs + 1),
775        t1,
776        format_angle_value(&r.angle),
777        t1,
778        r.stops.get_hash(),
779        t,
780    )
781}
782
783fn format_radial_gradient(r: &RadialGradient, tabs: usize) -> String {
784    let t = String::from("    ").repeat(tabs);
785    let t1 = String::from("    ").repeat(tabs + 1);
786    format!(
787        "RadialGradient {{\r\n{}shape: {},\r\n{}extend_mode: {},\r\n{}position: {},\r\n{}size: \
788         RadialGradientSize::{:?},\r\n{}stops: \
789         NormalizedLinearColorStopVec::from_const_slice(LINEAR_COLOR_STOP_{}_ITEMS),\r\n{}}}",
790        t1,
791        r.shape.format_as_rust_code(tabs + 1),
792        t1,
793        r.extend_mode.format_as_rust_code(tabs + 1),
794        t1,
795        format_style_background_position(&r.position, tabs + 1),
796        t1,
797        r.size,
798        t1,
799        r.stops.get_hash(),
800        t,
801    )
802}
803
804fn format_linear_color_stops(stops: &[NormalizedLinearColorStop], tabs: usize) -> String {
805    let t = String::from("    ").repeat(tabs);
806    stops
807        .iter()
808        .map(|s| format_linear_color_stop(s))
809        .collect::<Vec<_>>()
810        .join(&format!(",\r\n{}", t))
811}
812
813fn format_linear_color_stop(g: &NormalizedLinearColorStop) -> String {
814    format!(
815        "NormalizedLinearColorStop {{ offset: {}, color: {} }}",
816        format_percentage_value(&g.offset),
817        format_color_or_system(&g.color),
818    )
819}
820
821fn format_radial_color_stops(stops: &[NormalizedRadialColorStop], tabs: usize) -> String {
822    let t = String::from("    ").repeat(tabs);
823    stops
824        .iter()
825        .map(|s| format_radial_color_stop(s))
826        .collect::<Vec<_>>()
827        .join(&format!(",\r\n{}", t))
828}
829
830fn format_radial_color_stop(g: &NormalizedRadialColorStop) -> String {
831    format!(
832        "RadialColorStop {{ angle: {}, color: {} }}",
833        format_angle_value(&g.angle),
834        format_color_or_system(&g.color),
835    )
836}
837
838fn format_style_filter(st: &StyleFilter, tabs: usize) -> String {
839    let tabs_minus_one = String::from("    ").repeat(tabs);
840    let tabs_str = String::from("    ").repeat(tabs + 1);
841    match st {
842        StyleFilter::Blend(mb) => format!("StyleFilter::Blend({})", mb.format_as_rust_code(tabs)),
843        StyleFilter::Flood(c) => format!("StyleFilter::Flood({})", format_color_value(c)),
844        StyleFilter::Blur(m) => format!(
845            "StyleFilter::Blur(StyleBlur {{ width: {}, height: {} }})",
846            format_pixel_value(&m.width),
847            format_pixel_value(&m.height)
848        ),
849        StyleFilter::Opacity(pct) => {
850            format!("StyleFilter::Opacity({})", format_percentage_value(pct))
851        }
852        StyleFilter::ColorMatrix(cm) => format!(
853            "StyleFilter::ColorMatrix(StyleColorMatrix {{ m0: {}, m1: {}, m2: {}, m3: {}, m4: {}, \
854             m5: {}, m6: {}, m7: {}, m8: {}, m9: {}, m10: {}, m11: {}, m12: {}, m13: {}, m14: {}, \
855             m15: {}, m16: {}, m17: {}, m18: {}, m19: {} }})",
856            format_float_value(&cm.m0),
857            format_float_value(&cm.m1),
858            format_float_value(&cm.m2),
859            format_float_value(&cm.m3),
860            format_float_value(&cm.m4),
861            format_float_value(&cm.m5),
862            format_float_value(&cm.m6),
863            format_float_value(&cm.m7),
864            format_float_value(&cm.m8),
865            format_float_value(&cm.m9),
866            format_float_value(&cm.m10),
867            format_float_value(&cm.m11),
868            format_float_value(&cm.m12),
869            format_float_value(&cm.m13),
870            format_float_value(&cm.m14),
871            format_float_value(&cm.m15),
872            format_float_value(&cm.m16),
873            format_float_value(&cm.m17),
874            format_float_value(&cm.m18),
875            format_float_value(&cm.m19)
876        ),
877        StyleFilter::DropShadow(m) => {
878            format!("StyleFilter::DropShadow({})", m.format_as_rust_code(tabs))
879        }
880        StyleFilter::ComponentTransfer => format!("StyleFilter::ComponentTransfer"),
881        StyleFilter::Offset(o) => format!(
882            "StyleFilter::Offset(StyleFilterOffset {{ x: {}, y: {} }})",
883            format_pixel_value(&o.x),
884            format_pixel_value(&o.y)
885        ),
886        StyleFilter::Composite(StyleCompositeFilter::Over) => {
887            format!("StyleFilter::Composite(StyleCompositeFilter::Over)")
888        }
889        StyleFilter::Composite(StyleCompositeFilter::In) => {
890            format!("StyleFilter::Composite(StyleCompositeFilter::In)")
891        }
892        StyleFilter::Composite(StyleCompositeFilter::Atop) => {
893            format!("StyleFilter::Composite(StyleCompositeFilter::Atop)")
894        }
895        StyleFilter::Composite(StyleCompositeFilter::Out) => {
896            format!("StyleFilter::Composite(StyleCompositeFilter::Out)")
897        }
898        StyleFilter::Composite(StyleCompositeFilter::Xor) => {
899            format!("StyleFilter::Composite(StyleCompositeFilter::Xor)")
900        }
901        StyleFilter::Composite(StyleCompositeFilter::Lighter) => {
902            format!("StyleFilter::Composite(StyleCompositeFilter::Lighter)")
903        }
904        StyleFilter::Composite(StyleCompositeFilter::Arithmetic(fv)) => format!(
905            "StyleFilter::Composite(StyleCompositeFilter::Arithmetic(ArithmeticCoefficients {{ \
906             k1: {}, k2: {}, k3: {}, k4: {} }}))",
907            format_float_value(&fv.k1),
908            format_float_value(&fv.k2),
909            format_float_value(&fv.k3),
910            format_float_value(&fv.k4)
911        ),
912        StyleFilter::Brightness(v) => format!("StyleFilter::Brightness({})", format_percentage_value(v)),
913        StyleFilter::Contrast(v) => format!("StyleFilter::Contrast({})", format_percentage_value(v)),
914        StyleFilter::Grayscale(v) => format!("StyleFilter::Grayscale({})", format_percentage_value(v)),
915        StyleFilter::HueRotate(a) => format!("StyleFilter::HueRotate({})", format_angle_value(a)),
916        StyleFilter::Invert(v) => format!("StyleFilter::Invert({})", format_percentage_value(v)),
917        StyleFilter::Saturate(v) => format!("StyleFilter::Saturate({})", format_percentage_value(v)),
918        StyleFilter::Sepia(v) => format!("StyleFilter::Sepia({})", format_percentage_value(v)),
919    }
920}
921
922fn format_style_transforms(stops: &[StyleTransform], tabs: usize) -> String {
923    let t = String::from("    ").repeat(tabs);
924    stops
925        .iter()
926        .map(|s| format_style_transform(s, tabs))
927        .collect::<Vec<_>>()
928        .join(&format!(",\r\n{}", t))
929}
930
931fn format_style_transform(st: &StyleTransform, tabs: usize) -> String {
932    let tabs_minus_one = String::from("    ").repeat(tabs);
933    let tabs = String::from("    ").repeat(tabs + 1);
934    match st {
935        StyleTransform::Matrix(m) => format!(
936            "StyleTransform::Matrix(StyleTransformMatrix2D {{ a: {}, b: {}, c: {}, d: {}, tx: {}, \
937             ty: {} }})",
938            format_float_value(&m.a),
939            format_float_value(&m.b),
940            format_float_value(&m.c),
941            format_float_value(&m.d),
942            format_float_value(&m.tx),
943            format_float_value(&m.ty)
944        ),
945        StyleTransform::Matrix3D(m) => format!(
946            "StyleTransform::Matrix3D(StyleTransformMatrix3D {{\r\n{tabs}m11: {},\r\n{tabs}m12: \
947             {},\r\n{tabs}m13: {},\r\n{tabs}m14: {},\r\n{tabs}m21: {},\r\n{tabs}m22: \
948             {},\r\n{tabs}m23: {},\r\n{tabs}m24: {},\r\n{tabs}m31: {},\r\n{tabs}m32: \
949             {},\r\n{tabs}m33: {},\r\n{tabs}m34: {},\r\n{tabs}m41: {},\r\n{tabs}m42: \
950             {},\r\n{tabs}m43: {},\r\n{tabs}m44: {}\r\n{tabs_minus_one}}})",
951            format_float_value(&m.m11),
952            format_float_value(&m.m12),
953            format_float_value(&m.m13),
954            format_float_value(&m.m14),
955            format_float_value(&m.m21),
956            format_float_value(&m.m22),
957            format_float_value(&m.m23),
958            format_float_value(&m.m24),
959            format_float_value(&m.m31),
960            format_float_value(&m.m32),
961            format_float_value(&m.m33),
962            format_float_value(&m.m34),
963            format_float_value(&m.m41),
964            format_float_value(&m.m42),
965            format_float_value(&m.m43),
966            format_float_value(&m.m44),
967            tabs = tabs,
968            tabs_minus_one = tabs_minus_one,
969        ),
970        StyleTransform::Translate(t) => format!(
971            "StyleTransform::Translate(StyleTransformTranslate2D {{ x: {}, y: {} }})",
972            format_pixel_value(&t.x),
973            format_pixel_value(&t.y)
974        ),
975        StyleTransform::Translate3D(t) => format!(
976            "StyleTransform::Translate3D(StyleTransformTranslate3D {{ x: {}, y: {}, z: {})",
977            format_pixel_value(&t.x),
978            format_pixel_value(&t.y),
979            format_pixel_value(&t.z)
980        ),
981        StyleTransform::TranslateX(x) => {
982            format!("StyleTransform::TranslateX({})", format_pixel_value(&x))
983        }
984        StyleTransform::TranslateY(y) => {
985            format!("StyleTransform::TranslateY({})", format_pixel_value(&y))
986        }
987        StyleTransform::TranslateZ(z) => {
988            format!("StyleTransform::TranslateZ({})", format_pixel_value(&z))
989        }
990        StyleTransform::Rotate(r) => format!("StyleTransform::Rotate({})", format_angle_value(&r)),
991        StyleTransform::Rotate3D(r) => format!(
992            "StyleTransform::Rotate3D(StyleTransformRotate3D {{ {}, {}, {}, {} }})",
993            format_float_value(&r.x),
994            format_float_value(&r.y),
995            format_float_value(&r.z),
996            format_angle_value(&r.angle)
997        ),
998        StyleTransform::RotateX(x) => {
999            format!("StyleTransform::RotateX({})", format_angle_value(&x))
1000        }
1001        StyleTransform::RotateY(y) => {
1002            format!("StyleTransform::RotateY({})", format_angle_value(&y))
1003        }
1004        StyleTransform::RotateZ(z) => {
1005            format!("StyleTransform::RotateZ({})", format_angle_value(&z))
1006        }
1007        StyleTransform::Scale(s) => format!(
1008            "StyleTransform::Scale(StyleTransformScale2D {{ x: {}, y: {} }})",
1009            format_float_value(&s.x),
1010            format_float_value(&s.y)
1011        ),
1012        StyleTransform::Scale3D(s) => format!(
1013            "StyleTransform::Scale3D(StyleTransformScale3D {{ x; {}, y: {}, z: {} }})",
1014            format_float_value(&s.x),
1015            format_float_value(&s.y),
1016            format_float_value(&s.z)
1017        ),
1018        StyleTransform::ScaleX(x) => {
1019            format!("StyleTransform::ScaleX({})", format_percentage_value(&x))
1020        }
1021        StyleTransform::ScaleY(y) => {
1022            format!("StyleTransform::ScaleY({})", format_percentage_value(&y))
1023        }
1024        StyleTransform::ScaleZ(z) => {
1025            format!("StyleTransform::ScaleZ({})", format_percentage_value(&z))
1026        }
1027        StyleTransform::Skew(sk) => format!(
1028            "StyleTransform::Skew(StyleTransformSkew2D {{ x: {}, y: {} }})",
1029            format_angle_value(&sk.x),
1030            format_angle_value(&sk.y)
1031        ),
1032        StyleTransform::SkewX(x) => {
1033            format!("StyleTransform::SkewX({})", format_angle_value(&x))
1034        }
1035        StyleTransform::SkewY(y) => {
1036            format!("StyleTransform::SkewY({})", format_angle_value(&y))
1037        }
1038        StyleTransform::Perspective(dist) => {
1039            format!("StyleTransform::Perspective({})", format_pixel_value(&dist))
1040        }
1041    }
1042}
1043
1044fn format_font_ids(font_ids: &[StyleFontFamily], tabs: usize) -> String {
1045    let t = String::from("    ").repeat(tabs);
1046    font_ids
1047        .iter()
1048        .map(|s| format!("{}", s.format_as_rust_code(tabs + 1)))
1049        .collect::<Vec<_>>()
1050        .join(&format!(",\r\n{}", t))
1051}
1052
1053fn format_style_background_position(b: &StyleBackgroundPosition, tabs: usize) -> String {
1054    let t = String::from("    ").repeat(tabs);
1055    let t1 = String::from("    ").repeat(tabs + 1);
1056    format!(
1057        "StyleBackgroundPosition {{\r\n{}horizontal: {},\r\n{}vertical: {},\r\n{}}}",
1058        t1,
1059        format_background_position_horizontal(&b.horizontal),
1060        t1,
1061        format_background_position_vertical(&b.vertical),
1062        t
1063    )
1064}
1065
1066fn format_background_position_horizontal(b: &BackgroundPositionHorizontal) -> String {
1067    match b {
1068        BackgroundPositionHorizontal::Left => format!("BackgroundPositionHorizontal::Left"),
1069        BackgroundPositionHorizontal::Center => format!("BackgroundPositionHorizontal::Center"),
1070        BackgroundPositionHorizontal::Right => format!("BackgroundPositionHorizontal::Right"),
1071        BackgroundPositionHorizontal::Exact(p) => format!(
1072            "BackgroundPositionHorizontal::Exact({})",
1073            format_pixel_value(p)
1074        ),
1075    }
1076}
1077
1078fn format_background_position_vertical(b: &BackgroundPositionVertical) -> String {
1079    match b {
1080        BackgroundPositionVertical::Top => format!("BackgroundPositionVertical::Top"),
1081        BackgroundPositionVertical::Center => format!("BackgroundPositionVertical::Center"),
1082        BackgroundPositionVertical::Bottom => format!("BackgroundPositionVertical::Bottom"),
1083        BackgroundPositionVertical::Exact(p) => format!(
1084            "BackgroundPositionVertical::Exact({})",
1085            format_pixel_value(p)
1086        ),
1087    }
1088}
1089
1090// Border side style impls moved to `props/style/border.rs`.
1091
1092// Several small impls were moved to their respective modules.
1093
1094// StyleTransformOrigin impl moved to `props/style/transform.rs`.
1095
1096// Implementations for small property types were moved to their respective
1097// modules to keep the implementations next to the type definitions.