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