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 let current = cursor.current();
327 let parent = if position.get() == Position::FirstChild {
328 current
329 } else {
330 Rndr::get_parent(¤t)
331 .expect("first child of keyed list has no parent")
332 };
333 let parent = crate::renderer::types::Element::cast_from(parent)
334 .expect("parent of keyed list should be an element");
335
336 let items = self.items.into_iter();
338 let (capacity, _) = items.size_hint();
339 let mut hashed_items =
340 FxIndexSet::with_capacity_and_hasher(capacity, Default::default());
341 let mut rendered_items = Vec::new();
342 for (index, item) in items.enumerate() {
343 hashed_items.insert((self.key_fn)(&item));
344 let (set_index, view) = (self.view_fn)(index, item);
345 let item = view.hydrate::<FROM_SERVER>(cursor, position);
346 rendered_items.push(Some((set_index, item)));
347 }
348 let marker = cursor.next_placeholder(position);
349 position.set(Position::NextChild);
350
351 KeyedState {
352 parent: Some(parent),
353 marker,
354 hashed_items,
355 rendered_items,
356 }
357 }
358
359 async fn hydrate_async(
360 self,
361 cursor: &Cursor,
362 position: &PositionState,
363 ) -> Self::State {
364 let current = cursor.current();
366 let parent = if position.get() == Position::FirstChild {
367 current
368 } else {
369 Rndr::get_parent(¤t)
370 .expect("first child of keyed list has no parent")
371 };
372 let parent = crate::renderer::types::Element::cast_from(parent)
373 .expect("parent of keyed list should be an element");
374
375 let items = self.items.into_iter();
377 let (capacity, _) = items.size_hint();
378 let mut hashed_items =
379 FxIndexSet::with_capacity_and_hasher(capacity, Default::default());
380 let mut rendered_items = Vec::new();
381 for (index, item) in items.enumerate() {
382 hashed_items.insert((self.key_fn)(&item));
383 let (set_index, view) = (self.view_fn)(index, item);
384 let item = view.hydrate_async(cursor, position).await;
385 rendered_items.push(Some((set_index, item)));
386 }
387 let marker = cursor.next_placeholder(position);
388 position.set(Position::NextChild);
389
390 KeyedState {
391 parent: Some(parent),
392 marker,
393 hashed_items,
394 rendered_items,
395 }
396 }
397
398 fn into_owned(self) -> Self::Owned {
399 self
400 }
401}
402
403impl<K, VFS, V> Mountable for KeyedState<K, VFS, V>
404where
405 K: Eq + Hash + 'static,
406 VFS: Fn(usize),
407 V: Render,
408{
409 fn mount(
410 &mut self,
411 parent: &crate::renderer::types::Element,
412 marker: Option<&crate::renderer::types::Node>,
413 ) {
414 self.parent = Some(parent.clone());
415 for (_, item) in self.rendered_items.iter_mut().flatten() {
416 item.mount(parent, marker);
417 }
418 self.marker.mount(parent, marker);
419 }
420
421 fn unmount(&mut self) {
422 for (_, item) in self.rendered_items.iter_mut().flatten() {
423 item.unmount();
424 }
425 self.marker.unmount();
426 }
427
428 fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
429 self.rendered_items
430 .first()
431 .map(|item| {
432 if let Some((_, item)) = item {
433 item.insert_before_this(child)
434 } else {
435 false
436 }
437 })
438 .unwrap_or_else(|| self.marker.insert_before_this(child))
439 }
440
441 fn elements(&self) -> Vec<crate::renderer::types::Element> {
442 self.rendered_items
443 .iter()
444 .flatten()
445 .flat_map(|item| item.1.elements())
446 .collect()
447 }
448}
449
450trait VecExt<T> {
451 fn get_next_closest_mounted_sibling(
452 &self,
453 start_at: usize,
454 ) -> Option<&Option<T>>;
455}
456
457impl<T> VecExt<T> for Vec<Option<T>> {
458 fn get_next_closest_mounted_sibling(
459 &self,
460 start_at: usize,
461 ) -> Option<&Option<T>> {
462 self[start_at..].iter().find(|s| s.is_some())
463 }
464}
465
466fn diff<K: Eq + Hash>(from: &FxIndexSet<K>, to: &FxIndexSet<K>) -> Diff {
468 if from.is_empty() && to.is_empty() {
469 return Diff::default();
470 } else if to.is_empty() {
471 return Diff {
472 clear: true,
473 ..Default::default()
474 };
475 } else if from.is_empty() {
476 return Diff {
477 added: to
478 .iter()
479 .enumerate()
480 .map(|(at, _)| DiffOpAdd {
481 at,
482 mode: DiffOpAddMode::Append,
483 })
484 .collect(),
485 ..Default::default()
486 };
487 }
488
489 let mut removed = vec![];
490 let mut moved = vec![];
491 let mut added = vec![];
492 let max_len = std::cmp::max(from.len(), to.len());
493
494 for index in 0..max_len {
495 let from_item = from.get_index(index);
496 let to_item = to.get_index(index);
497
498 if from_item != to_item {
500 if from_item.is_some() && !to.contains(from_item.unwrap()) {
502 let op = DiffOpRemove { at: index };
503 removed.push(op);
504 }
505 if to_item.is_some() && !from.contains(to_item.unwrap()) {
507 let op = DiffOpAdd {
508 at: index,
509 mode: DiffOpAddMode::Normal,
510 };
511 added.push(op);
512 }
513 if let Some(from_item) = from_item {
519 if let Some(to_item) = to.get_full(from_item) {
520 let moves_forward_by = (to_item.0 as i32) - (index as i32);
521 let move_in_dom = moves_forward_by
522 != (added.len() as i32) - (removed.len() as i32);
523
524 let op = DiffOpMove {
525 from: index,
526 len: 1,
527 to: to_item.0,
528 move_in_dom,
529 };
530 moved.push(op);
531 }
532 }
533 }
534 }
535
536 moved = group_adjacent_moves(moved);
537
538 Diff {
539 removed,
540 items_to_move: moved.iter().map(|m| m.len).sum(),
541 moved,
542 added,
543 clear: false,
544 }
545}
546
547fn group_adjacent_moves(moved: Vec<DiffOpMove>) -> Vec<DiffOpMove> {
551 let mut prev: Option<DiffOpMove> = None;
552 let mut new_moved = Vec::with_capacity(moved.len());
553 for m in moved {
554 match prev {
555 Some(mut p) => {
556 if (m.from == p.from + p.len) && (m.to == p.to + p.len) {
557 p.len += 1;
558 prev = Some(p);
559 } else {
560 new_moved.push(prev.take().unwrap());
561 prev = Some(m);
562 }
563 }
564 None => prev = Some(m),
565 }
566 }
567 if let Some(prev) = prev {
568 new_moved.push(prev)
569 }
570 new_moved
571}
572
573#[derive(Debug, Default, PartialEq, Eq)]
574struct Diff {
575 removed: Vec<DiffOpRemove>,
576 moved: Vec<DiffOpMove>,
577 items_to_move: usize,
578 added: Vec<DiffOpAdd>,
579 clear: bool,
580}
581
582#[derive(Clone, Copy, Debug, PartialEq, Eq)]
583struct DiffOpMove {
584 from: usize,
586 len: usize,
588 to: usize,
590 move_in_dom: bool,
593}
594
595impl Default for DiffOpMove {
596 fn default() -> Self {
597 Self {
598 from: 0,
599 to: 0,
600 len: 1,
601 move_in_dom: true,
602 }
603 }
604}
605
606#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
607struct DiffOpAdd {
608 at: usize,
609 mode: DiffOpAddMode,
610}
611
612#[derive(Debug, PartialEq, Eq)]
613struct DiffOpRemove {
614 at: usize,
615}
616
617#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
618enum DiffOpAddMode {
619 #[default]
620 Normal,
621 Append,
622}
623
624fn apply_diff<T, VFS, V>(
625 parent: Option<&crate::renderer::types::Element>,
626 marker: &crate::renderer::types::Placeholder,
627 diff: Diff,
628 children: &mut Vec<Option<(VFS, V::State)>>,
629 view_fn: impl Fn(usize, T) -> (VFS, V),
630 mut items: Vec<Option<T>>,
631) where
632 VFS: Fn(usize),
633 V: Render,
634{
635 if diff.clear {
644 for (_, mut child) in children.drain(0..).flatten() {
645 child.unmount();
646 }
647
648 if diff.added.is_empty() {
649 return;
650 }
651 }
652
653 for DiffOpRemove { at } in &diff.removed {
654 let (_, mut item_to_remove) = children[*at].take().unwrap();
655
656 item_to_remove.unmount();
657 }
658
659 let (move_cmds, add_cmds) = unpack_moves(&diff);
660
661 let mut moved_children = move_cmds
662 .iter()
663 .map(|move_| children[move_.from].take())
664 .collect::<Vec<_>>();
665
666 children.resize_with(children.len() + diff.added.len(), || None);
667
668 for (i, DiffOpMove { to, .. }) in move_cmds
669 .iter()
670 .enumerate()
671 .filter(|(_, move_)| !move_.move_in_dom)
672 {
673 children[*to] = moved_children[i]
674 .take()
675 .inspect(|(set_index, _)| set_index(*to));
676 }
677
678 for (i, DiffOpMove { to, .. }) in move_cmds
679 .into_iter()
680 .enumerate()
681 .filter(|(_, move_)| move_.move_in_dom)
682 {
683 let (set_index, mut each_item) = moved_children[i].take().unwrap();
684
685 if let Some(parent) = parent {
686 if let Some(Some((_, state))) =
687 children.get_next_closest_mounted_sibling(to)
688 {
689 state.insert_before_this_or_marker(
690 parent,
691 &mut each_item,
692 Some(marker.as_ref()),
693 )
694 } else {
695 each_item.try_mount(parent, Some(marker.as_ref()));
696 }
697 }
698
699 set_index(to);
700 children[to] = Some((set_index, each_item));
701 }
702
703 for DiffOpAdd { at, mode } in add_cmds {
704 let item = items[at].take().unwrap();
705 let (set_index, item) = view_fn(at, item);
706 let mut item = item.build();
707
708 if let Some(parent) = parent {
709 match mode {
710 DiffOpAddMode::Normal => {
711 if let Some(Some((_, state))) =
712 children.get_next_closest_mounted_sibling(at)
713 {
714 state.insert_before_this_or_marker(
715 parent,
716 &mut item,
717 Some(marker.as_ref()),
718 )
719 } else {
720 item.try_mount(parent, Some(marker.as_ref()));
721 }
722 }
723 DiffOpAddMode::Append => {
724 item.try_mount(parent, Some(marker.as_ref()));
725 }
726 }
727 }
728
729 children[at] = Some((set_index, item));
730 }
731
732 #[allow(unstable_name_collisions)]
733 children.drain_filter(|c| c.is_none());
734}
735
736fn unpack_moves(diff: &Diff) -> (Vec<DiffOpMove>, Vec<DiffOpAdd>) {
737 let mut moves = Vec::with_capacity(diff.items_to_move);
738 let mut adds = Vec::with_capacity(diff.added.len());
739
740 let mut removes_iter = diff.removed.iter();
741 let mut adds_iter = diff.added.iter();
742 let mut moves_iter = diff.moved.iter();
743
744 let mut removes_next = removes_iter.next();
745 let mut adds_next = adds_iter.next();
746 let mut moves_next = moves_iter.next().copied();
747
748 for i in 0..diff.items_to_move + diff.added.len() + diff.removed.len() {
749 if let Some(DiffOpRemove { at, .. }) = removes_next {
750 if i == *at {
751 removes_next = removes_iter.next();
752
753 continue;
754 }
755 }
756
757 match (adds_next, &mut moves_next) {
758 (Some(add), Some(move_)) => {
759 if add.at == i {
760 adds.push(*add);
761
762 adds_next = adds_iter.next();
763 } else {
764 let mut single_move = *move_;
765 single_move.len = 1;
766
767 moves.push(single_move);
768
769 move_.len -= 1;
770 move_.from += 1;
771 move_.to += 1;
772
773 if move_.len == 0 {
774 moves_next = moves_iter.next().copied();
775 }
776 }
777 }
778 (Some(add), None) => {
779 adds.push(*add);
780
781 adds_next = adds_iter.next();
782 }
783 (None, Some(move_)) => {
784 let mut single_move = *move_;
785 single_move.len = 1;
786
787 moves.push(single_move);
788
789 move_.len -= 1;
790 move_.from += 1;
791 move_.to += 1;
792
793 if move_.len == 0 {
794 moves_next = moves_iter.next().copied();
795 }
796 }
797 (None, None) => break,
798 }
799 }
800
801 (moves, adds)
802}
803