Skip to main content

azul_css/
format_rust_code.rs

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