1use super::{
2 add_attr::AddAnyAttr, MarkBranch, Mountable, Position, PositionState,
3 Render, RenderHtml,
4};
5use crate::{
6 html::attribute::{any_attribute::AnyAttribute, Attribute},
7 hydration::Cursor,
8 renderer::{CastFrom, Rndr},
9 ssr::StreamBuilder,
10};
11use drain_filter_polyfill::VecExt as VecDrainFilterExt;
12use indexmap::IndexSet;
13use rustc_hash::FxHasher;
14use std::hash::{BuildHasherDefault, Hash};
15
16type FxIndexSet<T> = IndexSet<T, BuildHasherDefault<FxHasher>>;
17
18pub fn keyed<T, I, K, KF, VF, VFS, V>(
20 items: I,
21 key_fn: KF,
22 view_fn: VF,
23) -> Keyed<T, I, K, KF, VF, VFS, V>
24where
25 I: IntoIterator<Item = T>,
26 K: Eq + Hash + SerializableKey + 'static,
27 KF: Fn(&T) -> K,
28 V: Render,
29 VF: Fn(usize, T) -> (VFS, V),
30 VFS: Fn(usize),
31{
32 Keyed {
33 #[cfg(not(feature = "ssr"))]
34 items: Some(items),
35 #[cfg(feature = "ssr")]
36 items: None,
37 #[cfg(feature = "ssr")]
38 ssr_items: items
39 .into_iter()
40 .enumerate()
41 .map(|(i, t)| {
42 let key = if cfg!(feature = "islands") {
43 let key = (key_fn)(&t);
44 key.ser_key()
45 } else {
46 String::new()
47 };
48 let (_, view) = (view_fn)(i, t);
49 (key, view)
50 })
51 .collect::<Vec<_>>(),
52 key_fn,
53 view_fn,
54 }
55}
56
57pub struct Keyed<T, I, K, KF, VF, VFS, V>
59where
60 I: IntoIterator<Item = T>,
61 K: Eq + Hash + 'static,
62 KF: Fn(&T) -> K,
63 VF: Fn(usize, T) -> (VFS, V),
64 VFS: Fn(usize),
65{
66 items: Option<I>,
67 #[cfg(feature = "ssr")]
68 ssr_items: Vec<(String, V)>,
69 key_fn: KF,
70 view_fn: VF,
71}
72
73pub trait SerializableKey {
83 fn ser_key(&self) -> String;
88}
89
90#[cfg(not(feature = "islands"))]
91impl<T> SerializableKey for T {
92 fn ser_key(&self) -> String {
93 panic!(
94 "SerializableKey called without the `islands` feature enabled. \
95 Something has gone wrong."
96 );
97 }
98}
99#[cfg(feature = "islands")]
100impl<T: serde::Serialize> SerializableKey for T {
101 fn ser_key(&self) -> String {
102 serde_json::to_string(self).expect("failed to serialize key")
103 }
104}
105
106pub struct KeyedState<K, VFS, V>
108where
109 K: Eq + Hash + 'static,
110 VFS: Fn(usize),
111 V: Render,
112{
113 parent: Option<crate::renderer::types::Element>,
114 marker: crate::renderer::types::Placeholder,
115 hashed_items: IndexSet<K, BuildHasherDefault<FxHasher>>,
116 rendered_items: Vec<Option<(VFS, V::State)>>,
117}
118
119impl<T, I, K, KF, VF, VFS, V> Render for Keyed<T, I, K, KF, VF, VFS, V>
120where
121 I: IntoIterator<Item = T>,
122 K: Eq + Hash + SerializableKey + 'static,
123 KF: Fn(&T) -> K,
124 V: Render,
125 VF: Fn(usize, T) -> (VFS, V),
126 VFS: Fn(usize),
127{
128 type State = KeyedState<K, VFS, V>;
129
130 fn build(self) -> Self::State {
131 let items = self.items.into_iter().flatten();
132 let (capacity, _) = items.size_hint();
133 let mut hashed_items =
134 FxIndexSet::with_capacity_and_hasher(capacity, Default::default());
135 let mut rendered_items = Vec::with_capacity(capacity);
136 for (index, item) in items.enumerate() {
137 hashed_items.insert((self.key_fn)(&item));
138 let (set_index, view) = (self.view_fn)(index, item);
139 rendered_items.push(Some((set_index, view.build())));
140 }
141 KeyedState {
142 parent: None,
143 marker: Rndr::create_placeholder(),
144 hashed_items,
145 rendered_items,
146 }
147 }
148
149 fn rebuild(self, state: &mut Self::State) {
150 let KeyedState {
151 parent,
152 marker,
153 hashed_items,
154 ref mut rendered_items,
155 } = state;
156 let new_items = self.items.into_iter().flatten();
157 let (capacity, _) = new_items.size_hint();
158 let mut new_hashed_items =
159 FxIndexSet::with_capacity_and_hasher(capacity, Default::default());
160
161 let mut items = Vec::new();
162 for item in new_items {
163 new_hashed_items.insert((self.key_fn)(&item));
164 items.push(Some(item));
165 }
166
167 let cmds = diff(hashed_items, &new_hashed_items);
168
169 apply_diff(
170 parent.as_ref(),
171 marker,
172 cmds,
173 rendered_items,
174 &self.view_fn,
175 items,
176 );
177
178 *hashed_items = new_hashed_items;
179 }
180}
181
182impl<T, I, K, KF, VF, VFS, V> AddAnyAttr for Keyed<T, I, K, KF, VF, VFS, V>
183where
184 I: IntoIterator<Item = T> + Send + 'static,
185 K: Eq + Hash + SerializableKey + 'static,
186 KF: Fn(&T) -> K + Send + 'static,
187 V: RenderHtml,
188 V: 'static,
189 VF: Fn(usize, T) -> (VFS, V) + Send + 'static,
190 VFS: Fn(usize) + 'static,
191 T: 'static,
192{
193 type Output<SomeNewAttr: Attribute> = Keyed<
194 T,
195 I,
196 K,
197 KF,
198 Box<
199 dyn Fn(
200 usize,
201 T,
202 ) -> (
203 VFS,
204 <V as AddAnyAttr>::Output<SomeNewAttr::CloneableOwned>,
205 ) + Send,
206 >,
207 VFS,
208 V::Output<SomeNewAttr::CloneableOwned>,
209 >;
210
211 fn add_any_attr<NewAttr: Attribute>(
212 self,
213 attr: NewAttr,
214 ) -> Self::Output<NewAttr>
215 where
216 Self::Output<NewAttr>: RenderHtml,
217 {
218 let Keyed {
219 items,
220 #[cfg(feature = "ssr")]
221 ssr_items,
222 key_fn,
223 view_fn,
224 } = self;
225 let attr = attr.into_cloneable_owned();
226 Keyed {
227 items,
228 key_fn,
229 #[cfg(feature = "ssr")]
230 ssr_items: ssr_items
231 .into_iter()
232 .map(|(k, v)| (k, v.add_any_attr(attr.clone())))
233 .collect(),
234 view_fn: Box::new(move |index, item| {
235 let (index, view) = view_fn(index, item);
236 (index, view.add_any_attr(attr.clone()))
237 }),
238 }
239 }
240}
241
242impl<T, I, K, KF, VF, VFS, V> RenderHtml for Keyed<T, I, K, KF, VF, VFS, V>
243where
244 I: IntoIterator<Item = T> + Send + 'static,
245 K: Eq + Hash + SerializableKey + 'static,
246 KF: Fn(&T) -> K + Send + 'static,
247 V: RenderHtml + 'static,
248 VF: Fn(usize, T) -> (VFS, V) + Send + 'static,
249 VFS: Fn(usize) + 'static,
250 T: 'static,
251{
252 type AsyncOutput = Vec<V::AsyncOutput>; type Owned = Self;
254
255 const MIN_LENGTH: usize = 0;
256
257 fn dry_resolve(&mut self) {
258 #[cfg(feature = "ssr")]
259 for view in &mut self.ssr_items {
260 view.dry_resolve();
261 }
262 }
263
264 async fn resolve(self) -> Self::AsyncOutput {
265 #[cfg(feature = "ssr")]
266 {
267 futures::future::join_all(
268 self.ssr_items.into_iter().map(|(_, view)| view.resolve()),
269 )
270 .await
271 .into_iter()
272 .collect::<Vec<_>>()
273 }
274 #[cfg(not(feature = "ssr"))]
275 {
276 futures::future::join_all(
277 self.items.into_iter().flatten().enumerate().map(
278 |(index, item)| {
279 let (_, view) = (self.view_fn)(index, item);
280 view.resolve()
281 },
282 ),
283 )
284 .await
285 .into_iter()
286 .collect::<Vec<_>>()
287 }
288 }
289
290 #[allow(unused)]
291 fn to_html_with_buf(
292 self,
293 buf: &mut String,
294 position: &mut Position,
295 escape: bool,
296 mark_branches: bool,
297 extra_attrs: Vec<AnyAttribute>,
298 ) {
299 if mark_branches && escape {
300 buf.open_branch("for");
301 }
302
303 #[cfg(feature = "ssr")]
304 for item in self.ssr_items {
305 if mark_branches && escape {
306 buf.open_branch("item");
307 }
308 item.to_html_with_buf(
309 buf,
310 position,
311 escape,
312 mark_branches,
313 extra_attrs.clone(),
314 );
315 if mark_branches && escape {
316 buf.close_branch("item");
317 }
318 *position = Position::NextChild;
319 }
320 if mark_branches && escape {
321 buf.close_branch("for");
322 }
323 buf.push_str("<!>");
324 }
325
326 #[allow(unused)]
327 fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
328 self,
329 buf: &mut StreamBuilder,
330 position: &mut Position,
331 escape: bool,
332 mark_branches: bool,
333 extra_attrs: Vec<AnyAttribute>,
334 ) {
335 if mark_branches && escape {
336 buf.open_branch("for");
337 }
338
339 #[cfg(feature = "ssr")]
340 for (key, item) in self.ssr_items {
341 let branch_name = mark_branches.then(|| format!("item-{key}"));
342 if mark_branches && escape {
343 buf.open_branch(branch_name.as_ref().unwrap());
344 }
345 item.to_html_async_with_buf::<OUT_OF_ORDER>(
346 buf,
347 position,
348 escape,
349 mark_branches,
350 extra_attrs.clone(),
351 );
352 if mark_branches && escape {
353 buf.close_branch(branch_name.as_ref().unwrap());
354 }
355 *position = Position::NextChild;
356 }
357
358 if mark_branches && escape {
359 buf.close_branch("for");
360 }
361 buf.push_sync("<!>");
362 }
363
364 fn hydrate<const FROM_SERVER: bool>(
365 self,
366 cursor: &Cursor,
367 position: &PositionState,
368 ) -> Self::State {
369 let current = cursor.current();
371 let parent = if position.get() == Position::FirstChild {
372 current
373 } else {
374 Rndr::get_parent(¤t)
375 .expect("first child of keyed list has no parent")
376 };
377 let parent = crate::renderer::types::Element::cast_from(parent)
378 .expect("parent of keyed list should be an element");
379
380 let items = self.items.into_iter().flatten();
382 let (capacity, _) = items.size_hint();
383 let mut hashed_items =
384 FxIndexSet::with_capacity_and_hasher(capacity, Default::default());
385 let mut rendered_items = Vec::with_capacity(capacity);
386 for (index, item) in items.enumerate() {
387 hashed_items.insert((self.key_fn)(&item));
388 let (set_index, view) = (self.view_fn)(index, item);
389 let item = view.hydrate::<FROM_SERVER>(cursor, position);
390 rendered_items.push(Some((set_index, item)));
391 }
392 let marker = cursor.next_placeholder(position);
393 position.set(Position::NextChild);
394
395 KeyedState {
396 parent: Some(parent),
397 marker,
398 hashed_items,
399 rendered_items,
400 }
401 }
402
403 async fn hydrate_async(
404 self,
405 cursor: &Cursor,
406 position: &PositionState,
407 ) -> Self::State {
408 let current = cursor.current();
410 let parent = if position.get() == Position::FirstChild {
411 current
412 } else {
413 Rndr::get_parent(¤t)
414 .expect("first child of keyed list has no parent")
415 };
416 let parent = crate::renderer::types::Element::cast_from(parent)
417 .expect("parent of keyed list should be an element");
418
419 let items = self.items.into_iter().flatten();
421 let (capacity, _) = items.size_hint();
422 let mut hashed_items =
423 FxIndexSet::with_capacity_and_hasher(capacity, Default::default());
424 let mut rendered_items = Vec::with_capacity(capacity);
425 for (index, item) in items.enumerate() {
426 hashed_items.insert((self.key_fn)(&item));
427 let (set_index, view) = (self.view_fn)(index, item);
428 let item = view.hydrate_async(cursor, position).await;
429 rendered_items.push(Some((set_index, item)));
430 }
431 let marker = cursor.next_placeholder(position);
432 position.set(Position::NextChild);
433
434 KeyedState {
435 parent: Some(parent),
436 marker,
437 hashed_items,
438 rendered_items,
439 }
440 }
441
442 fn into_owned(self) -> Self::Owned {
443 self
444 }
445}
446
447impl<K, VFS, V> Mountable for KeyedState<K, VFS, V>
448where
449 K: Eq + Hash + 'static,
450 VFS: Fn(usize),
451 V: Render,
452{
453 fn mount(
454 &mut self,
455 parent: &crate::renderer::types::Element,
456 marker: Option<&crate::renderer::types::Node>,
457 ) {
458 self.parent = Some(parent.clone());
459 for (_, item) in self.rendered_items.iter_mut().flatten() {
460 item.mount(parent, marker);
461 }
462 self.marker.mount(parent, marker);
463 }
464
465 fn unmount(&mut self) {
466 for (_, item) in self.rendered_items.iter_mut().flatten() {
467 item.unmount();
468 }
469 self.marker.unmount();
470 }
471
472 fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
473 self.rendered_items
474 .first()
475 .map(|item| {
476 if let Some((_, item)) = item {
477 item.insert_before_this(child)
478 } else {
479 false
480 }
481 })
482 .unwrap_or_else(|| self.marker.insert_before_this(child))
483 }
484
485 fn elements(&self) -> Vec<crate::renderer::types::Element> {
486 self.rendered_items
487 .iter()
488 .flatten()
489 .flat_map(|item| item.1.elements())
490 .collect()
491 }
492}
493
494trait VecExt<T> {
495 fn get_next_closest_mounted_sibling(
496 &self,
497 start_at: usize,
498 ) -> Option<&Option<T>>;
499}
500
501impl<T> VecExt<T> for Vec<Option<T>> {
502 fn get_next_closest_mounted_sibling(
503 &self,
504 start_at: usize,
505 ) -> Option<&Option<T>> {
506 self[start_at..].iter().find(|s| s.is_some())
507 }
508}
509
510fn diff<K: Eq + Hash>(from: &FxIndexSet<K>, to: &FxIndexSet<K>) -> Diff {
512 if from.is_empty() && to.is_empty() {
513 return Diff::default();
514 } else if to.is_empty() {
515 return Diff {
516 clear: true,
517 ..Default::default()
518 };
519 } else if from.is_empty() {
520 return Diff {
521 added: to
522 .iter()
523 .enumerate()
524 .map(|(at, _)| DiffOpAdd {
525 at,
526 mode: DiffOpAddMode::Append,
527 })
528 .collect(),
529 ..Default::default()
530 };
531 }
532
533 let mut removed = vec![];
534 let mut moved = vec![];
535 let mut added = vec![];
536 let max_len = std::cmp::max(from.len(), to.len());
537
538 for index in 0..max_len {
539 let from_item = from.get_index(index);
540 let to_item = to.get_index(index);
541
542 if from_item != to_item {
544 if from_item.is_some() && !to.contains(from_item.unwrap()) {
546 let op = DiffOpRemove { at: index };
547 removed.push(op);
548 }
549 if to_item.is_some() && !from.contains(to_item.unwrap()) {
551 let op = DiffOpAdd {
552 at: index,
553 mode: DiffOpAddMode::Normal,
554 };
555 added.push(op);
556 }
557 if let Some(from_item) = from_item {
563 if let Some(to_item) = to.get_full(from_item) {
564 let moves_forward_by = (to_item.0 as i32) - (index as i32);
565 let move_in_dom = moves_forward_by
566 != (added.len() as i32) - (removed.len() as i32);
567
568 let op = DiffOpMove {
569 from: index,
570 len: 1,
571 to: to_item.0,
572 move_in_dom,
573 };
574 moved.push(op);
575 }
576 }
577 }
578 }
579
580 moved = group_adjacent_moves(moved);
581
582 Diff {
583 removed,
584 items_to_move: moved.iter().map(|m| m.len).sum(),
585 moved,
586 added,
587 clear: false,
588 }
589}
590
591fn group_adjacent_moves(moved: Vec<DiffOpMove>) -> Vec<DiffOpMove> {
595 let mut prev: Option<DiffOpMove> = None;
596 let mut new_moved = Vec::with_capacity(moved.len());
597 for m in moved {
598 match prev {
599 Some(mut p) => {
600 if (m.from == p.from + p.len) && (m.to == p.to + p.len) {
601 p.len += 1;
602 prev = Some(p);
603 } else {
604 new_moved.push(prev.take().unwrap());
605 prev = Some(m);
606 }
607 }
608 None => prev = Some(m),
609 }
610 }
611 if let Some(prev) = prev {
612 new_moved.push(prev)
613 }
614 new_moved
615}
616
617#[derive(Debug, Default, PartialEq, Eq)]
618struct Diff {
619 removed: Vec<DiffOpRemove>,
620 moved: Vec<DiffOpMove>,
621 items_to_move: usize,
622 added: Vec<DiffOpAdd>,
623 clear: bool,
624}
625
626#[derive(Clone, Copy, Debug, PartialEq, Eq)]
627struct DiffOpMove {
628 from: usize,
630 len: usize,
632 to: usize,
634 move_in_dom: bool,
637}
638
639impl Default for DiffOpMove {
640 fn default() -> Self {
641 Self {
642 from: 0,
643 to: 0,
644 len: 1,
645 move_in_dom: true,
646 }
647 }
648}
649
650#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
651struct DiffOpAdd {
652 at: usize,
653 mode: DiffOpAddMode,
654}
655
656#[derive(Debug, PartialEq, Eq)]
657struct DiffOpRemove {
658 at: usize,
659}
660
661#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
662enum DiffOpAddMode {
663 #[default]
664 Normal,
665 Append,
666}
667
668fn apply_diff<T, VFS, V>(
669 parent: Option<&crate::renderer::types::Element>,
670 marker: &crate::renderer::types::Placeholder,
671 diff: Diff,
672 children: &mut Vec<Option<(VFS, V::State)>>,
673 view_fn: &dyn Fn(usize, T) -> (VFS, V),
674 mut items: Vec<Option<T>>,
675) where
676 VFS: Fn(usize),
677 V: Render,
678{
679 if diff.clear {
688 for (_, mut child) in children.drain(0..).flatten() {
689 child.unmount();
690 }
691
692 if diff.added.is_empty() {
693 return;
694 }
695 }
696
697 for DiffOpRemove { at } in &diff.removed {
698 let (_, mut item_to_remove) = children[*at].take().unwrap();
699
700 item_to_remove.unmount();
701 }
702
703 let (move_cmds, add_cmds) = unpack_moves(&diff);
704
705 let mut moved_children = move_cmds
706 .iter()
707 .map(|move_| children[move_.from].take())
708 .collect::<Vec<_>>();
709
710 children.resize_with(children.len() + diff.added.len(), || None);
711
712 for (i, DiffOpMove { to, .. }) in move_cmds
713 .iter()
714 .enumerate()
715 .filter(|(_, move_)| !move_.move_in_dom)
716 {
717 children[*to] = moved_children[i]
718 .take()
719 .inspect(|(set_index, _)| set_index(*to));
720 }
721
722 for (i, DiffOpMove { to, .. }) in move_cmds
723 .into_iter()
724 .enumerate()
725 .filter(|(_, move_)| move_.move_in_dom)
726 {
727 let (set_index, mut each_item) = moved_children[i].take().unwrap();
728
729 if let Some(parent) = parent {
730 if let Some(Some((_, state))) =
731 children.get_next_closest_mounted_sibling(to)
732 {
733 state.insert_before_this_or_marker(
734 parent,
735 &mut each_item,
736 Some(marker.as_ref()),
737 )
738 } else {
739 each_item.try_mount(parent, Some(marker.as_ref()));
740 }
741 }
742
743 set_index(to);
744 children[to] = Some((set_index, each_item));
745 }
746
747 for DiffOpAdd { at, mode } in add_cmds {
748 let item = items[at].take().unwrap();
749 let (set_index, item) = view_fn(at, item);
750 let mut item = item.build();
751
752 if let Some(parent) = parent {
753 match mode {
754 DiffOpAddMode::Normal => {
755 if let Some(Some((_, state))) =
756 children.get_next_closest_mounted_sibling(at)
757 {
758 state.insert_before_this_or_marker(
759 parent,
760 &mut item,
761 Some(marker.as_ref()),
762 )
763 } else {
764 item.try_mount(parent, Some(marker.as_ref()));
765 }
766 }
767 DiffOpAddMode::Append => {
768 item.try_mount(parent, Some(marker.as_ref()));
769 }
770 }
771 }
772
773 children[at] = Some((set_index, item));
774 }
775
776 #[allow(unstable_name_collisions)]
777 children.drain_filter(|c| c.is_none());
778}
779
780fn unpack_moves(diff: &Diff) -> (Vec<DiffOpMove>, Vec<DiffOpAdd>) {
781 let mut moves = Vec::with_capacity(diff.items_to_move);
782 let mut adds = Vec::with_capacity(diff.added.len());
783
784 let mut removes_iter = diff.removed.iter();
785 let mut adds_iter = diff.added.iter();
786 let mut moves_iter = diff.moved.iter();
787
788 let mut removes_next = removes_iter.next();
789 let mut adds_next = adds_iter.next();
790 let mut moves_next = moves_iter.next().copied();
791
792 for i in 0..diff.items_to_move + diff.added.len() + diff.removed.len() {
793 if let Some(DiffOpRemove { at, .. }) = removes_next {
794 if i == *at {
795 removes_next = removes_iter.next();
796
797 continue;
798 }
799 }
800
801 match (adds_next, &mut moves_next) {
802 (Some(add), Some(move_)) => {
803 if add.at == i {
804 adds.push(*add);
805
806 adds_next = adds_iter.next();
807 } else {
808 let mut single_move = *move_;
809 single_move.len = 1;
810
811 moves.push(single_move);
812
813 move_.len -= 1;
814 move_.from += 1;
815 move_.to += 1;
816
817 if move_.len == 0 {
818 moves_next = moves_iter.next().copied();
819 }
820 }
821 }
822 (Some(add), None) => {
823 adds.push(*add);
824
825 adds_next = adds_iter.next();
826 }
827 (None, Some(move_)) => {
828 let mut single_move = *move_;
829 single_move.len = 1;
830
831 moves.push(single_move);
832
833 move_.len -= 1;
834 move_.from += 1;
835 move_.to += 1;
836
837 if move_.len == 0 {
838 moves_next = moves_iter.next().copied();
839 }
840 }
841 (None, None) => break,
842 }
843 }
844
845 (moves, adds)
846}
847