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#[allow(missing_docs)]
180#[derive(PartialEq, Clone, Debug)]
181pub enum AttributeOrProperty {
182 Static(&'static str),
189 Attribute(AttrValue),
190 Property(JsValue),
191}
192
193#[derive(PartialEq, Clone, Debug)]
195pub enum Attributes {
196 Static(&'static [(&'static str, AttributeOrProperty)]),
201
202 Dynamic {
208 keys: &'static [&'static str],
210
211 values: Box<[Option<AttributeOrProperty>]>,
214 },
215
216 IndexMap(Rc<IndexMap<AttrValue, AttributeOrProperty>>),
219}
220
221impl Attributes {
222 pub fn new() -> Self {
224 Self::default()
225 }
226
227 pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (&'a str, &'a str)> + 'a> {
232 match self {
233 Self::Static(arr) => Box::new(arr.iter().filter_map(|(k, v)| match v {
234 AttributeOrProperty::Attribute(v) => Some((*k, v.as_ref())),
235 AttributeOrProperty::Property(_) => None,
236 AttributeOrProperty::Static(v) => Some((*k, v)),
237 })),
238 Self::Dynamic { keys, values } => {
239 Box::new(keys.iter().zip(values.iter()).filter_map(|(k, v)| match v {
240 Some(AttributeOrProperty::Attribute(v)) => Some((*k, v.as_ref())),
241 _ => None,
242 }))
243 }
244 Self::IndexMap(m) => Box::new(m.iter().filter_map(|(k, v)| match v {
245 AttributeOrProperty::Attribute(v) => Some((k.as_ref(), v.as_ref())),
246 _ => None,
247 })),
248 }
249 }
250
251 pub fn get_mut_index_map(&mut self) -> &mut IndexMap<AttrValue, AttributeOrProperty> {
254 macro_rules! unpack {
255 () => {
256 match self {
257 Self::IndexMap(m) => Rc::make_mut(m),
258 _ => unsafe { unreachable_unchecked() },
260 }
261 };
262 }
263
264 match self {
265 Self::IndexMap(m) => Rc::make_mut(m),
266 Self::Static(arr) => {
267 *self = Self::IndexMap(Rc::new(
268 arr.iter().map(|(k, v)| ((*k).into(), v.clone())).collect(),
269 ));
270 unpack!()
271 }
272 Self::Dynamic { keys, values } => {
273 *self = Self::IndexMap(Rc::new(
274 std::mem::take(values)
275 .iter_mut()
276 .zip(keys.iter())
277 .filter_map(|(v, k)| v.take().map(|v| (AttrValue::from(*k), v)))
278 .collect(),
279 ));
280 unpack!()
281 }
282 }
283 }
284}
285
286impl From<IndexMap<AttrValue, AttrValue>> for Attributes {
287 fn from(map: IndexMap<AttrValue, AttrValue>) -> Self {
288 let v = map
289 .into_iter()
290 .map(|(k, v)| (k, AttributeOrProperty::Attribute(v)))
291 .collect();
292 Self::IndexMap(Rc::new(v))
293 }
294}
295
296impl From<IndexMap<&'static str, AttrValue>> for Attributes {
297 fn from(v: IndexMap<&'static str, AttrValue>) -> Self {
298 let v = v
299 .into_iter()
300 .map(|(k, v)| (AttrValue::Static(k), (AttributeOrProperty::Attribute(v))))
301 .collect();
302 Self::IndexMap(Rc::new(v))
303 }
304}
305
306impl From<IndexMap<&'static str, JsValue>> for Attributes {
307 fn from(v: IndexMap<&'static str, JsValue>) -> Self {
308 let v = v
309 .into_iter()
310 .map(|(k, v)| (AttrValue::Static(k), (AttributeOrProperty::Property(v))))
311 .collect();
312 Self::IndexMap(Rc::new(v))
313 }
314}
315
316impl Default for Attributes {
317 fn default() -> Self {
318 Self::Static(&[])
319 }
320}