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 fn into_owned(self) -> Self::Owned {
374 self
375 }
376}
377
378impl<K, VFS, V> Mountable for KeyedState<K, VFS, V>
379where
380 K: Eq + Hash + 'static,
381 VFS: Fn(usize),
382 V: Render,
383{
384 fn mount(
385 &mut self,
386 parent: &crate::renderer::types::Element,
387 marker: Option<&crate::renderer::types::Node>,
388 ) {
389 self.parent = Some(parent.clone());
390 for (_, item) in self.rendered_items.iter_mut().flatten() {
391 item.mount(parent, marker);
392 }
393 self.marker.mount(parent, marker);
394 }
395
396 fn unmount(&mut self) {
397 for (_, item) in self.rendered_items.iter_mut().flatten() {
398 item.unmount();
399 }
400 self.marker.unmount();
401 }
402
403 fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
404 self.rendered_items
405 .first()
406 .map(|item| {
407 if let Some((_, item)) = item {
408 item.insert_before_this(child)
409 } else {
410 false
411 }
412 })
413 .unwrap_or_else(|| self.marker.insert_before_this(child))
414 }
415
416 fn elements(&self) -> Vec<crate::renderer::types::Element> {
417 self.rendered_items
418 .iter()
419 .flatten()
420 .flat_map(|item| item.1.elements())
421 .collect()
422 }
423}
424
425trait VecExt<T> {
426 fn get_next_closest_mounted_sibling(
427 &self,
428 start_at: usize,
429 ) -> Option<&Option<T>>;
430}
431
432impl<T> VecExt<T> for Vec<Option<T>> {
433 fn get_next_closest_mounted_sibling(
434 &self,
435 start_at: usize,
436 ) -> Option<&Option<T>> {
437 self[start_at..].iter().find(|s| s.is_some())
438 }
439}
440
441fn diff<K: Eq + Hash>(from: &FxIndexSet<K>, to: &FxIndexSet<K>) -> Diff {
443 if from.is_empty() && to.is_empty() {
444 return Diff::default();
445 } else if to.is_empty() {
446 return Diff {
447 clear: true,
448 ..Default::default()
449 };
450 } else if from.is_empty() {
451 return Diff {
452 added: to
453 .iter()
454 .enumerate()
455 .map(|(at, _)| DiffOpAdd {
456 at,
457 mode: DiffOpAddMode::Append,
458 })
459 .collect(),
460 ..Default::default()
461 };
462 }
463
464 let mut removed = vec![];
465 let mut moved = vec![];
466 let mut added = vec![];
467 let max_len = std::cmp::max(from.len(), to.len());
468
469 for index in 0..max_len {
470 let from_item = from.get_index(index);
471 let to_item = to.get_index(index);
472
473 if from_item != to_item {
475 if from_item.is_some() && !to.contains(from_item.unwrap()) {
477 let op = DiffOpRemove { at: index };
478 removed.push(op);
479 }
480 if to_item.is_some() && !from.contains(to_item.unwrap()) {
482 let op = DiffOpAdd {
483 at: index,
484 mode: DiffOpAddMode::Normal,
485 };
486 added.push(op);
487 }
488 if let Some(from_item) = from_item {
494 if let Some(to_item) = to.get_full(from_item) {
495 let moves_forward_by = (to_item.0 as i32) - (index as i32);
496 let move_in_dom = moves_forward_by
497 != (added.len() as i32) - (removed.len() as i32);
498
499 let op = DiffOpMove {
500 from: index,
501 len: 1,
502 to: to_item.0,
503 move_in_dom,
504 };
505 moved.push(op);
506 }
507 }
508 }
509 }
510
511 moved = group_adjacent_moves(moved);
512
513 Diff {
514 removed,
515 items_to_move: moved.iter().map(|m| m.len).sum(),
516 moved,
517 added,
518 clear: false,
519 }
520}
521
522fn group_adjacent_moves(moved: Vec<DiffOpMove>) -> Vec<DiffOpMove> {
526 let mut prev: Option<DiffOpMove> = None;
527 let mut new_moved = Vec::with_capacity(moved.len());
528 for m in moved {
529 match prev {
530 Some(mut p) => {
531 if (m.from == p.from + p.len) && (m.to == p.to + p.len) {
532 p.len += 1;
533 prev = Some(p);
534 } else {
535 new_moved.push(prev.take().unwrap());
536 prev = Some(m);
537 }
538 }
539 None => prev = Some(m),
540 }
541 }
542 if let Some(prev) = prev {
543 new_moved.push(prev)
544 }
545 new_moved
546}
547
548#[derive(Debug, Default, PartialEq, Eq)]
549struct Diff {
550 removed: Vec<DiffOpRemove>,
551 moved: Vec<DiffOpMove>,
552 items_to_move: usize,
553 added: Vec<DiffOpAdd>,
554 clear: bool,
555}
556
557#[derive(Clone, Copy, Debug, PartialEq, Eq)]
558struct DiffOpMove {
559 from: usize,
561 len: usize,
563 to: usize,
565 move_in_dom: bool,
568}
569
570impl Default for DiffOpMove {
571 fn default() -> Self {
572 Self {
573 from: 0,
574 to: 0,
575 len: 1,
576 move_in_dom: true,
577 }
578 }
579}
580
581#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
582struct DiffOpAdd {
583 at: usize,
584 mode: DiffOpAddMode,
585}
586
587#[derive(Debug, PartialEq, Eq)]
588struct DiffOpRemove {
589 at: usize,
590}
591
592#[derive(Clone, Copy, Debug, PartialEq, Eq)]
593enum DiffOpAddMode {
594 Normal,
595 Append,
596}
597
598impl Default for DiffOpAddMode {
599 fn default() -> Self {
600 Self::Normal
601 }
602}
603
604fn apply_diff<T, VFS, V>(
605 parent: Option<&crate::renderer::types::Element>,
606 marker: &crate::renderer::types::Placeholder,
607 diff: Diff,
608 children: &mut Vec<Option<(VFS, V::State)>>,
609 view_fn: impl Fn(usize, T) -> (VFS, V),
610 mut items: Vec<Option<T>>,
611) where
612 VFS: Fn(usize),
613 V: Render,
614{
615 if diff.clear {
624 for (_, mut child) in children.drain(0..).flatten() {
625 child.unmount();
626 }
627
628 if diff.added.is_empty() {
629 return;
630 }
631 }
632
633 for DiffOpRemove { at } in &diff.removed {
634 let (_, mut item_to_remove) = children[*at].take().unwrap();
635
636 item_to_remove.unmount();
637 }
638
639 let (move_cmds, add_cmds) = unpack_moves(&diff);
640
641 let mut moved_children = move_cmds
642 .iter()
643 .map(|move_| children[move_.from].take())
644 .collect::<Vec<_>>();
645
646 children.resize_with(children.len() + diff.added.len(), || None);
647
648 for (i, DiffOpMove { to, .. }) in move_cmds
649 .iter()
650 .enumerate()
651 .filter(|(_, move_)| !move_.move_in_dom)
652 {
653 children[*to] = moved_children[i]
654 .take()
655 .inspect(|(set_index, _)| set_index(*to));
656 }
657
658 for (i, DiffOpMove { to, .. }) in move_cmds
659 .into_iter()
660 .enumerate()
661 .filter(|(_, move_)| move_.move_in_dom)
662 {
663 let (set_index, mut each_item) = moved_children[i].take().unwrap();
664
665 if let Some(parent) = parent {
666 if let Some(Some((_, state))) =
667 children.get_next_closest_mounted_sibling(to)
668 {
669 state.insert_before_this_or_marker(
670 parent,
671 &mut each_item,
672 Some(marker.as_ref()),
673 )
674 } else {
675 each_item.mount(parent, Some(marker.as_ref()));
676 }
677 }
678
679 set_index(to);
680 children[to] = Some((set_index, each_item));
681 }
682
683 for DiffOpAdd { at, mode } in add_cmds {
684 let item = items[at].take().unwrap();
685 let (set_index, item) = view_fn(at, item);
686 let mut item = item.build();
687
688 if let Some(parent) = parent {
689 match mode {
690 DiffOpAddMode::Normal => {
691 if let Some(Some((_, state))) =
692 children.get_next_closest_mounted_sibling(at)
693 {
694 state.insert_before_this_or_marker(
695 parent,
696 &mut item,
697 Some(marker.as_ref()),
698 )
699 } else {
700 item.mount(parent, Some(marker.as_ref()));
701 }
702 }
703 DiffOpAddMode::Append => {
704 item.mount(parent, Some(marker.as_ref()));
705 }
706 }
707 }
708
709 children[at] = Some((set_index, item));
710 }
711
712 #[allow(unstable_name_collisions)]
713 children.drain_filter(|c| c.is_none());
714}
715
716fn unpack_moves(diff: &Diff) -> (Vec<DiffOpMove>, Vec<DiffOpAdd>) {
717 let mut moves = Vec::with_capacity(diff.items_to_move);
718 let mut adds = Vec::with_capacity(diff.added.len());
719
720 let mut removes_iter = diff.removed.iter();
721 let mut adds_iter = diff.added.iter();
722 let mut moves_iter = diff.moved.iter();
723
724 let mut removes_next = removes_iter.next();
725 let mut adds_next = adds_iter.next();
726 let mut moves_next = moves_iter.next().copied();
727
728 for i in 0..diff.items_to_move + diff.added.len() + diff.removed.len() {
729 if let Some(DiffOpRemove { at, .. }) = removes_next {
730 if i == *at {
731 removes_next = removes_iter.next();
732
733 continue;
734 }
735 }
736
737 match (adds_next, &mut moves_next) {
738 (Some(add), Some(move_)) => {
739 if add.at == i {
740 adds.push(*add);
741
742 adds_next = adds_iter.next();
743 } else {
744 let mut single_move = *move_;
745 single_move.len = 1;
746
747 moves.push(single_move);
748
749 move_.len -= 1;
750 move_.from += 1;
751 move_.to += 1;
752
753 if move_.len == 0 {
754 moves_next = moves_iter.next().copied();
755 }
756 }
757 }
758 (Some(add), None) => {
759 adds.push(*add);
760
761 adds_next = adds_iter.next();
762 }
763 (None, Some(move_)) => {
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 (None, None) => break,
778 }
779 }
780
781 (moves, adds)
782}
783