1#[doc(hidden)]
4pub mod key;
5#[doc(hidden)]
6pub mod listeners;
7#[doc(hidden)]
8pub mod vcomp;
9#[doc(hidden)]
10pub mod vlist;
11#[doc(hidden)]
12pub mod vnode;
13#[doc(hidden)]
14pub mod vportal;
15#[doc(hidden)]
16pub mod vraw;
17#[doc(hidden)]
18pub mod vsuspense;
19#[doc(hidden)]
20pub mod vtag;
21#[doc(hidden)]
22pub mod vtext;
23
24use std::hint::unreachable_unchecked;
25use std::rc::Rc;
26
27use indexmap::IndexMap;
28use wasm_bindgen::JsValue;
29
30#[doc(inline)]
31pub use self::key::Key;
32#[doc(inline)]
33pub use self::listeners::*;
34#[doc(inline)]
35pub use self::vcomp::{VChild, VComp};
36#[doc(hidden)]
37pub use self::vlist::FullyKeyedState;
38#[doc(inline)]
39pub use self::vlist::VList;
40#[doc(inline)]
41pub use self::vnode::VNode;
42#[doc(inline)]
43pub use self::vportal::VPortal;
44#[doc(inline)]
45pub use self::vraw::VRaw;
46#[doc(inline)]
47pub use self::vsuspense::VSuspense;
48#[doc(inline)]
49pub use self::vtag::VTag;
50#[doc(inline)]
51pub use self::vtext::VText;
52
53pub type AttrValue = implicit_clone::unsync::IString;
55
56#[cfg(any(feature = "ssr", feature = "hydration"))]
57mod feat_ssr_hydration {
58 #[cfg(debug_assertions)]
59 type ComponentName = &'static str;
60 #[cfg(not(debug_assertions))]
61 type ComponentName = std::marker::PhantomData<()>;
62
63 #[cfg(feature = "hydration")]
64 use std::borrow::Cow;
65
66 pub enum Collectable {
70 Component(ComponentName),
71 Raw,
72 Suspense,
73 }
74
75 impl Collectable {
76 #[cfg(not(debug_assertions))]
77 #[inline(always)]
78 pub fn for_component<T: 'static>() -> Self {
79 use std::marker::PhantomData;
80 let _comp_type: PhantomData<T> = PhantomData;
84 Self::Component(PhantomData)
85 }
86
87 #[cfg(debug_assertions)]
88 pub fn for_component<T: 'static>() -> Self {
89 let comp_name = std::any::type_name::<T>();
90 Self::Component(comp_name)
91 }
92
93 pub fn open_start_mark(&self) -> &'static str {
94 match self {
95 Self::Component(_) => "<[",
96 Self::Raw => "<#",
97 Self::Suspense => "<?",
98 }
99 }
100
101 pub fn close_start_mark(&self) -> &'static str {
102 match self {
103 Self::Component(_) => "</[",
104 Self::Raw => "</#",
105 Self::Suspense => "</?",
106 }
107 }
108
109 pub fn end_mark(&self) -> &'static str {
110 match self {
111 Self::Component(_) => "]>",
112 Self::Raw => ">",
113 Self::Suspense => ">",
114 }
115 }
116
117 #[cfg(feature = "hydration")]
118 pub fn name(&self) -> Cow<'static, str> {
119 match self {
120 #[cfg(debug_assertions)]
121 Self::Component(m) => format!("Component({m})").into(),
122 #[cfg(not(debug_assertions))]
123 Self::Component(_) => "Component".into(),
124 Self::Raw => "Raw".into(),
125 Self::Suspense => "Suspense".into(),
126 }
127 }
128 }
129}
130
131#[cfg(any(feature = "ssr", feature = "hydration"))]
132pub(crate) use feat_ssr_hydration::*;
133
134#[cfg(feature = "ssr")]
135mod feat_ssr {
136 use std::fmt::Write;
137
138 use super::*;
139 use crate::platform::fmt::BufWriter;
140
141 impl Collectable {
142 pub(crate) fn write_open_tag(&self, w: &mut BufWriter) {
143 let _ = w.write_str("<!--");
144 let _ = w.write_str(self.open_start_mark());
145
146 #[cfg(debug_assertions)]
147 match self {
148 Self::Component(type_name) => {
149 let _ = w.write_str(type_name);
150 }
151 Self::Raw => {}
152 Self::Suspense => {}
153 }
154
155 let _ = w.write_str(self.end_mark());
156 let _ = w.write_str("-->");
157 }
158
159 pub(crate) fn write_close_tag(&self, w: &mut BufWriter) {
160 let _ = w.write_str("<!--");
161 let _ = w.write_str(self.close_start_mark());
162
163 #[cfg(debug_assertions)]
164 match self {
165 Self::Component(type_name) => {
166 let _ = w.write_str(type_name);
167 }
168 Self::Raw => {}
169 Self::Suspense => {}
170 }
171
172 let _ = w.write_str(self.end_mark());
173 let _ = w.write_str("-->");
174 }
175 }
176}
177
178#[expect(missing_docs)]
180#[derive(PartialEq, Clone, Debug)]
181pub enum AttributeOrProperty {
182 Attribute(AttrValue),
183 Property(JsValue),
184}
185
186#[derive(PartialEq, Clone, Debug)]
188pub enum Attributes {
189 Static(&'static [(&'static str, AttributeOrProperty)]),
194
195 Dynamic {
201 keys: &'static [&'static str],
203
204 values: Box<[Option<AttributeOrProperty>]>,
207 },
208
209 IndexMap(Rc<IndexMap<AttrValue, AttributeOrProperty>>),
212}
213
214impl Attributes {
215 pub fn new() -> Self {
217 Self::default()
218 }
219
220 pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (&'a str, &'a str)> + 'a> {
225 match self {
226 Self::Static(arr) => Box::new(arr.iter().filter_map(|(k, v)| match v {
227 AttributeOrProperty::Attribute(v) => Some((*k, v.as_ref())),
228 AttributeOrProperty::Property(_) => None,
229 })),
230 Self::Dynamic { keys, values } => {
231 Box::new(keys.iter().zip(values.iter()).filter_map(|(k, v)| match v {
232 Some(AttributeOrProperty::Attribute(v)) => Some((*k, v.as_ref())),
233 _ => None,
234 }))
235 }
236 Self::IndexMap(m) => Box::new(m.iter().filter_map(|(k, v)| match v {
237 AttributeOrProperty::Attribute(v) => Some((k.as_ref(), v.as_ref())),
238 _ => None,
239 })),
240 }
241 }
242
243 pub fn get_mut_index_map(&mut self) -> &mut IndexMap<AttrValue, AttributeOrProperty> {
246 macro_rules! unpack {
247 () => {
248 match self {
249 Self::IndexMap(m) => Rc::make_mut(m),
250 _ => unsafe { unreachable_unchecked() },
252 }
253 };
254 }
255
256 match self {
257 Self::IndexMap(m) => Rc::make_mut(m),
258 Self::Static(arr) => {
259 *self = Self::IndexMap(Rc::new(
260 arr.iter().map(|(k, v)| ((*k).into(), v.clone())).collect(),
261 ));
262 unpack!()
263 }
264 Self::Dynamic { keys, values } => {
265 *self = Self::IndexMap(Rc::new(
266 std::mem::take(values)
267 .iter_mut()
268 .zip(keys.iter())
269 .filter_map(|(v, k)| v.take().map(|v| (AttrValue::from(*k), v)))
270 .collect(),
271 ));
272 unpack!()
273 }
274 }
275 }
276}
277
278impl From<IndexMap<AttrValue, AttrValue>> for Attributes {
279 fn from(map: IndexMap<AttrValue, AttrValue>) -> Self {
280 let v = map
281 .into_iter()
282 .map(|(k, v)| (k, AttributeOrProperty::Attribute(v)))
283 .collect();
284 Self::IndexMap(Rc::new(v))
285 }
286}
287
288impl From<IndexMap<&'static str, AttrValue>> for Attributes {
289 fn from(v: IndexMap<&'static str, AttrValue>) -> Self {
290 let v = v
291 .into_iter()
292 .map(|(k, v)| (AttrValue::Static(k), (AttributeOrProperty::Attribute(v))))
293 .collect();
294 Self::IndexMap(Rc::new(v))
295 }
296}
297
298impl From<IndexMap<&'static str, JsValue>> for Attributes {
299 fn from(v: IndexMap<&'static str, JsValue>) -> Self {
300 let v = v
301 .into_iter()
302 .map(|(k, v)| (AttrValue::Static(k), (AttributeOrProperty::Property(v))))
303 .collect();
304 Self::IndexMap(Rc::new(v))
305 }
306}
307
308impl Default for Attributes {
309 fn default() -> Self {
310 Self::Static(&[])
311 }
312}