1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/// Opt-in: lets a [`Display`](std::fmt::Display) type of your own be rendered by vertigo -
/// as an attribute value, and as a text node in [`dom!`](crate::dom).
///
/// Neither [`AttrValue`](crate::AttrValue) nor [`EmbedDom`](crate::EmbedDom) converts from
/// every `T: Display`, because a blanket impl over a foreign trait collides with everything:
///
/// * on the attribute side it would claim the whole of `AttrValue`'s `From` space, so nothing
/// else could ever convert into an attribute - [`TwClass`](crate::TwClass) has to keep
/// hiding from `ToString` for exactly this reason - and a reactive wrapper that happens to
/// print would be silently flattened to a snapshot instead of subscribing;
/// * on the embedding side it would collide with `impl EmbedDom for MyComponent`, so a
/// printable type could never render real DOM instead of a text node.
///
/// Gating the same blanket on a trait of vertigo's own keeps the convenience without either
/// collision: nothing is rendered until it says so, and saying so is one line.
///
/// ```rust
/// use vertigo::{DomDisplay, dom};
///
/// enum Route {
/// Home,
/// Post(u32),
/// }
///
/// impl std::fmt::Display for Route {
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// match self {
/// Route::Home => write!(f, "/"),
/// Route::Post(id) => write!(f, "/post/{id}"),
/// }
/// }
/// }
///
/// impl DomDisplay for Route {}
///
/// let route = Route::Post(7);
/// let link = dom! { <a href={&route}>{&route}</a> };
/// ```
///
/// That single impl covers every form the value arrives in: `Route`, `&Route`,
/// [`Computed<Route>`](crate::Computed), [`Value<Route>`](crate::Value), the optional
/// variants of those, and references to any of them.
///
/// A type which renders real DOM implements [`EmbedDom`](crate::EmbedDom) instead, and must
/// not implement this - the two are alternatives, and asking for both is a coherence error.
///
/// The advice lives here rather than only on the traits which fail, because those see
/// `&Route` where this sees `Route` - and `impl DomDisplay for &Route {}` is not the fix.
/// So one `impl DomDisplay for MyType {}` reaches `&MyType` as well, and a single blanket over
/// `T` serves both. Separate blankets over `T` and `&T` cannot: they overlap, because a
/// downstream crate may implement `DomDisplay` for a reference to a type of its own.
// The string-ish types are absent on purpose. Each already has conversions that beat
// `to_string` at its own job - keeping a `&'static str` static, or an `Rc<String>` shared -
// and a marker impl would collide with them.
impl_dom_display!;
/// The types behind the `chrono` feature, which vertigo already knows how to put on the wire.
///
/// Each renders as its `Display` - `2026-08-30`, `2026-08-30 14:03:11`, and the same with an
/// offset for a `DateTime`. That is ISO 8601, not a localized date; a template wanting one
/// formats it itself, as it always had to.
///
/// The set matches the `JsJson` impls the feature already carries, plus `NaiveTime` and any
/// timezone rather than only `Utc`, both of which come free. `Weekday`, `Month` and
/// `TimeDelta` print names and ISO durations rather than values, so they stay out.
/// `Decimal` behind the `rust_decimal` feature, for the same reason as the chrono types
/// above: the application cannot write this impl itself.