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
354// Macro implementations for common patterns
355
356macro_rules! impl_float_value_fmt {
357    ($struct_name:ident) => {
358        impl FormatAsRustCode for $struct_name {
359            fn format_as_rust_code(&self, _tabs: usize) -> String {
360                format!(
361                    "{} {{ inner: {} }}",
362                    stringify!($struct_name),
363                    format_float_value(&self.inner)
364                )
365            }
366        }
367    };
368}
369
370impl_float_value_fmt!(LayoutFlexGrow);
371impl_float_value_fmt!(LayoutFlexShrink);
372
373macro_rules! impl_percentage_value_fmt {
374    ($struct_name:ident) => {
375        impl FormatAsRustCode for $struct_name {
376            fn format_as_rust_code(&self, _tabs: usize) -> String {
377                format!(
378                    "{} {{ inner: {} }}",
379                    stringify!($struct_name),
380                    format_percentage_value(&self.inner)
381                )
382            }
383        }
384    };
385}
386
387impl_percentage_value_fmt!(StyleLineHeight);
388impl_percentage_value_fmt!(StyleOpacity);
389
390macro_rules! impl_pixel_value_fmt {
391    ($struct_name:ident) => {
392        impl FormatAsRustCode for $struct_name {
393            fn format_as_rust_code(&self, _tabs: usize) -> String {
394                format!(
395                    "{} {{ inner: {} }}",
396                    stringify!($struct_name),
397                    format_pixel_value(&self.inner)
398                )
399            }
400        }
401    };
402}
403
404impl_pixel_value_fmt!(StyleTabWidth);
405impl_pixel_value_fmt!(StyleBorderTopLeftRadius);
406impl_pixel_value_fmt!(StyleBorderBottomLeftRadius);
407impl_pixel_value_fmt!(StyleBorderTopRightRadius);
408impl_pixel_value_fmt!(StyleBorderBottomRightRadius);
409
410impl_pixel_value_fmt!(LayoutBorderTopWidth);
411impl_pixel_value_fmt!(LayoutBorderLeftWidth);
412impl_pixel_value_fmt!(LayoutBorderRightWidth);
413impl_pixel_value_fmt!(LayoutBorderBottomWidth);
414impl_pixel_value_fmt!(StyleLetterSpacing);
415impl_pixel_value_fmt!(StyleWordSpacing);
416impl_pixel_value_fmt!(StyleFontSize);
417
418impl_pixel_value_fmt!(LayoutMarginTop);
419impl_pixel_value_fmt!(LayoutMarginBottom);
420impl_pixel_value_fmt!(LayoutMarginRight);
421impl_pixel_value_fmt!(LayoutMarginLeft);
422
423impl_pixel_value_fmt!(LayoutPaddingTop);
424impl_pixel_value_fmt!(LayoutPaddingBottom);
425impl_pixel_value_fmt!(LayoutPaddingRight);
426impl_pixel_value_fmt!(LayoutPaddingLeft);
427impl_pixel_value_fmt!(LayoutPaddingInlineStart);
428impl_pixel_value_fmt!(LayoutPaddingInlineEnd);
429
430impl FormatAsRustCode for LayoutWidth {
431    fn format_as_rust_code(&self, _tabs: usize) -> String {
432        match self {
433            LayoutWidth::Auto => "LayoutWidth::Auto".to_string(),
434            LayoutWidth::Px(px) => format!("LayoutWidth::Px({})", format_pixel_value(px)),
435            LayoutWidth::MinContent => "LayoutWidth::MinContent".to_string(),
436            LayoutWidth::MaxContent => "LayoutWidth::MaxContent".to_string(),
437        }
438    }
439}
440
441impl FormatAsRustCode for LayoutHeight {
442    fn format_as_rust_code(&self, _tabs: usize) -> String {
443        match self {
444            LayoutHeight::Auto => "LayoutHeight::Auto".to_string(),
445            LayoutHeight::Px(px) => format!("LayoutHeight::Px({})", format_pixel_value(px)),
446            LayoutHeight::MinContent => "LayoutHeight::MinContent".to_string(),
447            LayoutHeight::MaxContent => "LayoutHeight::MaxContent".to_string(),
448        }
449    }
450}
451
452impl_pixel_value_fmt!(LayoutMinHeight);
453impl_pixel_value_fmt!(LayoutMinWidth);
454impl_pixel_value_fmt!(LayoutMaxWidth);
455impl_pixel_value_fmt!(LayoutMaxHeight);
456impl_pixel_value_fmt!(LayoutTop);
457impl_pixel_value_fmt!(LayoutInsetBottom);
458
459impl_pixel_value_fmt!(LayoutRight);
460impl_pixel_value_fmt!(LayoutLeft);
461
462// LayoutFlexBasis implementation moved to `props/layout/flex.rs`.
463
464impl_pixel_value_fmt!(LayoutColumnGap);
465impl_pixel_value_fmt!(LayoutRowGap);
466
467impl_grid_value_fmt!(GridTemplate);
468impl_grid_value_fmt!(GridPlacement);
469
470impl_color_value_fmt!(StyleTextColor);
471impl_color_value_fmt!(StyleBorderTopColor);
472impl_color_value_fmt!(StyleBorderLeftColor);
473impl_color_value_fmt!(StyleBorderRightColor);
474impl_color_value_fmt!(StyleBorderBottomColor);
475
476impl_enum_fmt!(
477    StyleMixBlendMode,
478    Normal,
479    Multiply,
480    Screen,
481    Overlay,
482    Darken,
483    Lighten,
484    ColorDodge,
485    ColorBurn,
486    HardLight,
487    SoftLight,
488    Difference,
489    Exclusion,
490    Hue,
491    Saturation,
492    Color,
493    Luminosity
494);
495
496impl_enum_fmt!(StyleHyphens, Auto, None);
497
498impl_enum_fmt!(StyleDirection, Ltr, Rtl);
499
500impl_enum_fmt!(StyleWhiteSpace, Normal, Pre, Nowrap);
501
502impl_enum_fmt!(StyleVisibility, Visible, Hidden, Collapse);
503
504impl_enum_fmt!(LayoutWritingMode, HorizontalTb, VerticalRl, VerticalLr);
505
506impl_enum_fmt!(LayoutClear, None, Left, Right, Both);
507
508impl_enum_fmt!(
509    StyleCursor,
510    Alias,
511    AllScroll,
512    Cell,
513    ColResize,
514    ContextMenu,
515    Copy,
516    Crosshair,
517    Default,
518    EResize,
519    EwResize,
520    Grab,
521    Grabbing,
522    Help,
523    Move,
524    NResize,
525    NsResize,
526    NeswResize,
527    NwseResize,
528    Pointer,
529    Progress,
530    RowResize,
531    SResize,
532    SeResize,
533    Text,
534    Unset,
535    VerticalText,
536    WResize,
537    Wait,
538    ZoomIn,
539    ZoomOut
540);
541
542impl_enum_fmt!(
543    BorderStyle,
544    None,
545    Solid,
546    Double,
547    Dotted,
548    Dashed,
549    Hidden,
550    Groove,
551    Ridge,
552    Inset,
553    Outset
554);
555
556impl_enum_fmt!(
557    StyleBackgroundRepeat,
558    NoRepeat,
559    PatternRepeat,
560    RepeatX,
561    RepeatY
562);
563
564impl_enum_fmt!(
565    LayoutDisplay,
566    None,
567    Block,
568    Inline,
569    InlineBlock,
570    Flex,
571    InlineFlex,
572    Table,
573    InlineTable,
574    TableRowGroup,
575    TableHeaderGroup,
576    TableFooterGroup,
577    TableRow,
578    TableColumnGroup,
579    TableColumn,
580    TableCell,
581    TableCaption,
582    ListItem,
583    RunIn,
584    Marker,
585    Grid,
586    InlineGrid,
587    FlowRoot
588);
589
590impl_enum_fmt!(LayoutFloat, Left, Right, None);
591
592impl_enum_fmt!(LayoutBoxSizing, ContentBox, BorderBox);
593
594impl_enum_fmt!(LayoutFlexDirection, Row, RowReverse, Column, ColumnReverse);
595
596impl_enum_fmt!(LayoutFlexWrap, Wrap, NoWrap, WrapReverse);
597
598impl_enum_fmt!(
599    LayoutJustifyContent,
600    Start,
601    End,
602    FlexStart,
603    FlexEnd,
604    Center,
605    SpaceBetween,
606    SpaceAround,
607    SpaceEvenly
608);
609
610impl_enum_fmt!(LayoutAlignItems, Stretch, Center, Start, End, Baseline);
611
612impl_enum_fmt!(
613    LayoutAlignContent,
614    Start,
615    End,
616    Stretch,
617    Center,
618    SpaceBetween,
619    SpaceAround
620);
621
622impl_enum_fmt!(Shape, Circle, Ellipse);
623
624impl_enum_fmt!(LayoutOverflow, Auto, Scroll, Visible, Hidden, Clip);
625
626impl_enum_fmt!(StyleTextAlign, Center, Left, Right, Justify, Start, End);
627
628impl_enum_fmt!(StyleUserSelect, Auto, Text, None, All);
629
630impl_enum_fmt!(StyleTextDecoration, None, Underline, Overline, LineThrough);
631
632impl_enum_fmt!(
633    DirectionCorner,
634    Right,
635    Left,
636    Top,
637    Bottom,
638    TopRight,
639    TopLeft,
640    BottomRight,
641    BottomLeft
642);
643
644impl_enum_fmt!(ExtendMode, Clamp, Repeat);
645
646impl_enum_fmt!(StyleBackfaceVisibility, Visible, Hidden);
647
648// Complex type implementations
649
650fn format_style_background_size(c: &StyleBackgroundSize) -> String {
651    match c {
652        StyleBackgroundSize::Contain => String::from("StyleBackgroundSize::Contain"),
653        StyleBackgroundSize::Cover => String::from("StyleBackgroundSize::Cover"),
654        StyleBackgroundSize::ExactSize(size) => format!(
655            "StyleBackgroundSize::ExactSize(PixelValueSize {{ width: {}, height: {} }})",
656            format_pixel_value(&size.width),
657            format_pixel_value(&size.height)
658        ),
659    }
660}
661
662// Scrollbar-related impls moved to `props/style/scrollbar.rs`
663
664pub fn format_scrollbar_info(s: &ScrollbarInfo, tabs: usize) -> String {
665    let t = String::from("    ").repeat(tabs);
666    let t1 = String::from("    ").repeat(tabs + 1);
667    format!(
668        "ScrollbarInfo {{\r\n{}width: {},\r\n{}padding_left: {},\r\n{}padding_right: \
669         {},\r\n{}track: {},\r\n{}thumb: {},\r\n{}button: {},\r\n{}button: {},\r\n{}resizer: \
670         {},\r\n{}}}",
671        t1,
672        s.width.format_as_rust_code(tabs + 1),
673        t1,
674        s.padding_left.format_as_rust_code(tabs + 1),
675        t1,
676        s.padding_right.format_as_rust_code(tabs + 1),
677        t1,
678        format_style_background_content(&s.track, tabs + 1),
679        t1,
680        format_style_background_content(&s.thumb, tabs + 1),
681        t1,
682        format_style_background_content(&s.button, tabs + 1),
683        t1,
684        format_style_background_content(&s.corner, tabs + 1),
685        t1,
686        format_style_background_content(&s.resizer, tabs + 1),
687        t
688    )
689}
690
691// Background/filter vec impls moved to their respective modules.
692
693fn format_style_background_content(content: &StyleBackgroundContent, tabs: usize) -> String {
694    match content {
695        StyleBackgroundContent::LinearGradient(l) => format!(
696            "StyleBackgroundContent::LinearGradient({})",
697            format_linear_gradient(l, tabs)
698        ),
699        StyleBackgroundContent::RadialGradient(r) => format!(
700            "StyleBackgroundContent::RadialGradient({})",
701            format_radial_gradient(r, tabs)
702        ),
703        StyleBackgroundContent::ConicGradient(r) => format!(
704            "StyleBackgroundContent::ConicGradient({})",
705            format_conic_gradient(r, tabs)
706        ),
707        StyleBackgroundContent::Image(id) => format!("StyleBackgroundContent::Image({:?})", id),
708        StyleBackgroundContent::Color(c) => {
709            format!("StyleBackgroundContent::Color({})", format_color_value(c))
710        }
711    }
712}
713
714fn format_direction(d: &Direction, tabs: usize) -> String {
715    match d {
716        Direction::Angle(fv) => format!("Direction::Angle({})", format_angle_value(fv)),
717        Direction::FromTo(DirectionCorners { dir_from, dir_to }) => format!(
718            "Direction::FromTo(DirectionCorners {{ dir_from: {}, dir_to: {} }})",
719            dir_from.format_as_rust_code(tabs + 1),
720            dir_to.format_as_rust_code(tabs + 1)
721        ),
722    }
723}
724
725fn format_linear_gradient(l: &LinearGradient, tabs: usize) -> String {
726    let t = String::from("    ").repeat(tabs);
727    let t1 = String::from("    ").repeat(tabs + 1);
728    format!(
729        "LinearGradient {{\r\n{}direction: {},\r\n{}extend_mode: {},\r\n{}stops: \
730         NormalizedLinearColorStopVec::from_const_slice(LINEAR_COLOR_STOP_{}_ITEMS),\r\n{}}}",
731        t1,
732        format_direction(&l.direction, tabs + 1),
733        t1,
734        l.extend_mode.format_as_rust_code(tabs + 1),
735        t1,
736        l.stops.get_hash(),
737        t,
738    )
739}
740
741fn format_conic_gradient(r: &ConicGradient, tabs: usize) -> String {
742    let t = String::from("    ").repeat(tabs);
743    let t1 = String::from("    ").repeat(tabs + 1);
744
745    format!(
746        "ConicGradient {{\r\n{}extend_mode: {},\r\n{}center: {},\r\n{}angle: {},\r\n{}stops: \
747         NormalizedRadialColorStopVec::from_const_slice(RADIAL_COLOR_STOP_{}_ITEMS),\r\n{}}}",
748        t1,
749        r.extend_mode.format_as_rust_code(tabs + 1),
750        t1,
751        format_style_background_position(&r.center, tabs + 1),
752        t1,
753        format_angle_value(&r.angle),
754        t1,
755        r.stops.get_hash(),
756        t,
757    )
758}
759
760fn format_radial_gradient(r: &RadialGradient, tabs: usize) -> String {
761    let t = String::from("    ").repeat(tabs);
762    let t1 = String::from("    ").repeat(tabs + 1);
763    format!(
764        "RadialGradient {{\r\n{}shape: {},\r\n{}extend_mode: {},\r\n{}position: {},\r\n{}size: \
765         RadialGradientSize::{:?},\r\n{}stops: \
766         NormalizedLinearColorStopVec::from_const_slice(LINEAR_COLOR_STOP_{}_ITEMS),\r\n{}}}",
767        t1,
768        r.shape.format_as_rust_code(tabs + 1),
769        t1,
770        r.extend_mode.format_as_rust_code(tabs + 1),
771        t1,
772        format_style_background_position(&r.position, tabs + 1),
773        t1,
774        r.size,
775        t1,
776        r.stops.get_hash(),
777        t,
778    )
779}
780
781fn format_linear_color_stops(stops: &[NormalizedLinearColorStop], tabs: usize) -> String {
782    let t = String::from("    ").repeat(tabs);
783    stops
784        .iter()
785        .map(|s| format_linear_color_stop(s))
786        .collect::<Vec<_>>()
787        .join(&format!(",\r\n{}", t))
788}
789
790fn format_linear_color_stop(g: &NormalizedLinearColorStop) -> String {
791    format!(
792        "NormalizedLinearColorStop {{ offset: {}, color: {} }}",
793        format_percentage_value(&g.offset),
794        format_color_value(&g.color),
795    )
796}
797
798fn format_radial_color_stops(stops: &[NormalizedRadialColorStop], tabs: usize) -> String {
799    let t = String::from("    ").repeat(tabs);
800    stops
801        .iter()
802        .map(|s| format_radial_color_stop(s))
803        .collect::<Vec<_>>()
804        .join(&format!(",\r\n{}", t))
805}
806
807fn format_radial_color_stop(g: &NormalizedRadialColorStop) -> String {
808    format!(
809        "RadialColorStop {{ angle: {}, color: {} }}",
810        format_angle_value(&g.angle),
811        format_color_value(&g.color),
812    )
813}
814
815fn format_style_filter(st: &StyleFilter, tabs: usize) -> String {
816    let tabs_minus_one = String::from("    ").repeat(tabs);
817    let tabs_str = String::from("    ").repeat(tabs + 1);
818    match st {
819        StyleFilter::Blend(mb) => format!("StyleFilter::Blend({})", mb.format_as_rust_code(tabs)),
820        StyleFilter::Flood(c) => format!("StyleFilter::Flood({})", format_color_value(c)),
821        StyleFilter::Blur(m) => format!(
822            "StyleFilter::Blur(StyleBlur {{ width: {}, height: {} }})",
823            format_pixel_value(&m.width),
824            format_pixel_value(&m.height)
825        ),
826        StyleFilter::Opacity(pct) => {
827            format!("StyleFilter::Opacity({})", format_percentage_value(pct))
828        }
829        StyleFilter::ColorMatrix(cm) => format!(
830            "StyleFilter::ColorMatrix(StyleColorMatrix {{ m0: {}, m1: {}, m2: {}, m3: {}, m4: {}, \
831             m5: {}, m6: {}, m7: {}, m8: {}, m9: {}, m10: {}, m11: {}, m12: {}, m13: {}, m14: {}, \
832             m15: {}, m16: {}, m17: {}, m18: {}, m19: {} }})",
833            format_float_value(&cm.m0),
834            format_float_value(&cm.m1),
835            format_float_value(&cm.m2),
836            format_float_value(&cm.m3),
837            format_float_value(&cm.m4),
838            format_float_value(&cm.m5),
839            format_float_value(&cm.m6),
840            format_float_value(&cm.m7),
841            format_float_value(&cm.m8),
842            format_float_value(&cm.m9),
843            format_float_value(&cm.m10),
844            format_float_value(&cm.m11),
845            format_float_value(&cm.m12),
846            format_float_value(&cm.m13),
847            format_float_value(&cm.m14),
848            format_float_value(&cm.m15),
849            format_float_value(&cm.m16),
850            format_float_value(&cm.m17),
851            format_float_value(&cm.m18),
852            format_float_value(&cm.m19)
853        ),
854        StyleFilter::DropShadow(m) => {
855            format!("StyleFilter::DropShadow({})", m.format_as_rust_code(tabs))
856        }
857        StyleFilter::ComponentTransfer => format!("StyleFilter::ComponentTransfer"),
858        StyleFilter::Offset(o) => format!(
859            "StyleFilter::Offset(StyleFilterOffset {{ x: {}, y: {} }})",
860            format_pixel_value(&o.x),
861            format_pixel_value(&o.y)
862        ),
863        StyleFilter::Composite(StyleCompositeFilter::Over) => {
864            format!("StyleFilter::Composite(StyleCompositeFilter::Over)")
865        }
866        StyleFilter::Composite(StyleCompositeFilter::In) => {
867            format!("StyleFilter::Composite(StyleCompositeFilter::In)")
868        }
869        StyleFilter::Composite(StyleCompositeFilter::Atop) => {
870            format!("StyleFilter::Composite(StyleCompositeFilter::Atop)")
871        }
872        StyleFilter::Composite(StyleCompositeFilter::Out) => {
873            format!("StyleFilter::Composite(StyleCompositeFilter::Out)")
874        }
875        StyleFilter::Composite(StyleCompositeFilter::Xor) => {
876            format!("StyleFilter::Composite(StyleCompositeFilter::Xor)")
877        }
878        StyleFilter::Composite(StyleCompositeFilter::Lighter) => {
879            format!("StyleFilter::Composite(StyleCompositeFilter::Lighter)")
880        }
881        StyleFilter::Composite(StyleCompositeFilter::Arithmetic(fv)) => format!(
882            "StyleFilter::Composite(StyleCompositeFilter::Arithmetic(ArithmeticCoefficients {{ \
883             k1: {}, k2: {}, k3: {}, k4: {} }}))",
884            format_float_value(&fv.k1),
885            format_float_value(&fv.k2),
886            format_float_value(&fv.k3),
887            format_float_value(&fv.k4)
888        ),
889    }
890}
891
892fn format_style_transforms(stops: &[StyleTransform], tabs: usize) -> String {
893    let t = String::from("    ").repeat(tabs);
894    stops
895        .iter()
896        .map(|s| format_style_transform(s, tabs))
897        .collect::<Vec<_>>()
898        .join(&format!(",\r\n{}", t))
899}
900
901fn format_style_transform(st: &StyleTransform, tabs: usize) -> String {
902    let tabs_minus_one = String::from("    ").repeat(tabs);
903    let tabs = String::from("    ").repeat(tabs + 1);
904    match st {
905        StyleTransform::Matrix(m) => format!(
906            "StyleTransform::Matrix(StyleTransformMatrix2D {{ a: {}, b: {}, c: {}, d: {}, tx: {}, \
907             ty: {} }})",
908            format_float_value(&m.a),
909            format_float_value(&m.b),
910            format_float_value(&m.c),
911            format_float_value(&m.d),
912            format_float_value(&m.tx),
913            format_float_value(&m.ty)
914        ),
915        StyleTransform::Matrix3D(m) => format!(
916            "StyleTransform::Matrix3D(StyleTransformMatrix3D {{\r\n{tabs}m11: {},\r\n{tabs}m12: \
917             {},\r\n{tabs}m13: {},\r\n{tabs}m14: {},\r\n{tabs}m21: {},\r\n{tabs}m22: \
918             {},\r\n{tabs}m23: {},\r\n{tabs}m24: {},\r\n{tabs}m31: {},\r\n{tabs}m32: \
919             {},\r\n{tabs}m33: {},\r\n{tabs}m34: {},\r\n{tabs}m41: {},\r\n{tabs}m42: \
920             {},\r\n{tabs}m43: {},\r\n{tabs}m44: {}\r\n{tabs_minus_one}}})",
921            format_float_value(&m.m11),
922            format_float_value(&m.m12),
923            format_float_value(&m.m13),
924            format_float_value(&m.m14),
925            format_float_value(&m.m21),
926            format_float_value(&m.m22),
927            format_float_value(&m.m23),
928            format_float_value(&m.m24),
929            format_float_value(&m.m31),
930            format_float_value(&m.m32),
931            format_float_value(&m.m33),
932            format_float_value(&m.m34),
933            format_float_value(&m.m41),
934            format_float_value(&m.m42),
935            format_float_value(&m.m43),
936            format_float_value(&m.m44),
937            tabs = tabs,
938            tabs_minus_one = tabs_minus_one,
939        ),
940        StyleTransform::Translate(t) => format!(
941            "StyleTransform::Translate(StyleTransformTranslate2D {{ x: {}, y: {} }})",
942            format_pixel_value(&t.x),
943            format_pixel_value(&t.y)
944        ),
945        StyleTransform::Translate3D(t) => format!(
946            "StyleTransform::Translate3D(StyleTransformTranslate3D {{ x: {}, y: {}, z: {})",
947            format_pixel_value(&t.x),
948            format_pixel_value(&t.y),
949            format_pixel_value(&t.z)
950        ),
951        StyleTransform::TranslateX(x) => {
952            format!("StyleTransform::TranslateX({})", format_pixel_value(&x))
953        }
954        StyleTransform::TranslateY(y) => {
955            format!("StyleTransform::TranslateY({})", format_pixel_value(&y))
956        }
957        StyleTransform::TranslateZ(z) => {
958            format!("StyleTransform::TranslateZ({})", format_pixel_value(&z))
959        }
960        StyleTransform::Rotate(r) => format!("StyleTransform::Rotate({})", format_angle_value(&r)),
961        StyleTransform::Rotate3D(r) => format!(
962            "StyleTransform::Rotate3D(StyleTransformRotate3D {{ {}, {}, {}, {} }})",
963            format_float_value(&r.x),
964            format_float_value(&r.y),
965            format_float_value(&r.z),
966            format_angle_value(&r.angle)
967        ),
968        StyleTransform::RotateX(x) => {
969            format!("StyleTransform::RotateX({})", format_angle_value(&x))
970        }
971        StyleTransform::RotateY(y) => {
972            format!("StyleTransform::RotateY({})", format_angle_value(&y))
973        }
974        StyleTransform::RotateZ(z) => {
975            format!("StyleTransform::RotateZ({})", format_angle_value(&z))
976        }
977        StyleTransform::Scale(s) => format!(
978            "StyleTransform::Scale(StyleTransformScale2D {{ x: {}, y: {} }})",
979            format_float_value(&s.x),
980            format_float_value(&s.y)
981        ),
982        StyleTransform::Scale3D(s) => format!(
983            "StyleTransform::Scale3D(StyleTransformScale3D {{ x; {}, y: {}, z: {} }})",
984            format_float_value(&s.x),
985            format_float_value(&s.y),
986            format_float_value(&s.z)
987        ),
988        StyleTransform::ScaleX(x) => {
989            format!("StyleTransform::ScaleX({})", format_percentage_value(&x))
990        }
991        StyleTransform::ScaleY(y) => {
992            format!("StyleTransform::ScaleY({})", format_percentage_value(&y))
993        }
994        StyleTransform::ScaleZ(z) => {
995            format!("StyleTransform::ScaleZ({})", format_percentage_value(&z))
996        }
997        StyleTransform::Skew(sk) => format!(
998            "StyleTransform::Skew(StyleTransformSkew2D {{ x: {}, y: {} }})",
999            format_angle_value(&sk.x),
1000            format_angle_value(&sk.y)
1001        ),
1002        StyleTransform::SkewX(x) => {
1003            format!("StyleTransform::SkewX({})", format_angle_value(&x))
1004        }
1005        StyleTransform::SkewY(y) => {
1006            format!("StyleTransform::SkewY({})", format_angle_value(&y))
1007        }
1008        StyleTransform::Perspective(dist) => {
1009            format!("StyleTransform::Perspective({})", format_pixel_value(&dist))
1010        }
1011    }
1012}
1013
1014fn format_font_ids(font_ids: &[StyleFontFamily], tabs: usize) -> String {
1015    let t = String::from("    ").repeat(tabs);
1016    font_ids
1017        .iter()
1018        .map(|s| format!("{}", s.format_as_rust_code(tabs + 1)))
1019        .collect::<Vec<_>>()
1020        .join(&format!(",\r\n{}", t))
1021}
1022
1023fn format_style_background_position(b: &StyleBackgroundPosition, tabs: usize) -> String {
1024    let t = String::from("    ").repeat(tabs);
1025    let t1 = String::from("    ").repeat(tabs + 1);
1026    format!(
1027        "StyleBackgroundPosition {{\r\n{}horizontal: {},\r\n{}vertical: {},\r\n{}}}",
1028        t1,
1029        format_background_position_horizontal(&b.horizontal),
1030        t1,
1031        format_background_position_vertical(&b.vertical),
1032        t
1033    )
1034}
1035
1036fn format_background_position_horizontal(b: &BackgroundPositionHorizontal) -> String {
1037    match b {
1038        BackgroundPositionHorizontal::Left => format!("BackgroundPositionHorizontal::Left"),
1039        BackgroundPositionHorizontal::Center => format!("BackgroundPositionHorizontal::Center"),
1040        BackgroundPositionHorizontal::Right => format!("BackgroundPositionHorizontal::Right"),
1041        BackgroundPositionHorizontal::Exact(p) => format!(
1042            "BackgroundPositionHorizontal::Exact({})",
1043            format_pixel_value(p)
1044        ),
1045    }
1046}
1047
1048fn format_background_position_vertical(b: &BackgroundPositionVertical) -> String {
1049    match b {
1050        BackgroundPositionVertical::Top => format!("BackgroundPositionVertical::Top"),
1051        BackgroundPositionVertical::Center => format!("BackgroundPositionVertical::Center"),
1052        BackgroundPositionVertical::Bottom => format!("BackgroundPositionVertical::Bottom"),
1053        BackgroundPositionVertical::Exact(p) => format!(
1054            "BackgroundPositionVertical::Exact({})",
1055            format_pixel_value(p)
1056        ),
1057    }
1058}
1059
1060// Border side style impls moved to `props/style/border.rs`.
1061
1062// Several small impls were moved to their respective modules.
1063
1064// StyleTransformOrigin impl moved to `props/style/transform.rs`.
1065
1066// Implementations for small property types were moved to their respective
1067// modules to keep the implementations next to the type definitions.