1use crate::browser::dom::Namespace;
74use crate::virtual_dom::{El, ElKey, Node, Tag, Text};
75use std::borrow::Borrow;
76use std::collections::{BTreeSet, VecDeque};
77use std::iter::Peekable;
78
79#[allow(clippy::large_enum_variant)]
80pub enum PatchCommand<'a, Ms: 'static> {
81 AppendEl {
82 el_new: &'a mut El<Ms>,
83 },
84 AppendText {
85 text_new: &'a mut Text,
86 },
87 InsertEl {
88 el_new: &'a mut El<Ms>,
89 next_node: web_sys::Node,
90 },
91 InsertText {
92 text_new: &'a mut Text,
93 next_node: web_sys::Node,
94 },
95 PatchEl {
96 el_old: El<Ms>,
97 el_new: &'a mut El<Ms>,
98 },
99 PatchText {
100 text_old: Text,
101 text_new: &'a mut Text,
102 },
103 ReplaceElByEl {
104 el_old: El<Ms>,
105 el_new: &'a mut El<Ms>,
106 },
107 ReplaceElByText {
108 el_old: El<Ms>,
109 text_new: &'a mut Text,
110 },
111 ReplaceTextByEl {
112 text_old: Text,
113 el_new: &'a mut El<Ms>,
114 },
115 RemoveEl {
116 el_old: El<Ms>,
117 },
118 RemoveText {
119 text_old: Text,
120 },
121}
122
123#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
127enum PatchKey {
128 Element {
129 namespace: Option<Namespace>,
130 tag: Tag,
131 el_key: Option<ElKey>,
132 },
133 Text,
134}
135
136impl PatchKey {
137 fn new<Ms: 'static>(node: &Node<Ms>) -> Option<Self> {
138 match node {
139 Node::Element(el) => Some(PatchKey::Element {
140 namespace: el.namespace.clone(),
141 tag: el.tag.clone(),
142 el_key: el.key.clone(),
143 }),
144 Node::Text(_) => Some(PatchKey::Text),
145 Node::Empty | Node::NoChange => None,
146 }
147 }
148}
149
150pub struct PatchGen<'a, Ms, OI, NI>
153where
154 Ms: 'static,
155 OI: Iterator<Item = Node<Ms>>,
156 NI: Iterator<Item = &'a mut Node<Ms>>,
157{
158 old_children_iter: Peekable<OI>,
159 new_children_iter: Peekable<NI>,
160 old_children: VecDeque<Node<Ms>>,
161 new_children: VecDeque<&'a mut Node<Ms>>,
162 matching_child_old: Option<OI::Item>,
163 matching_child_new: Option<NI::Item>,
164 matching_key: Option<PatchKey>,
165 keyed_mode: bool,
166}
167
168impl<'a, Ms, OI, NI> PatchGen<'a, Ms, OI, NI>
169where
170 Ms: 'static,
171 OI: Iterator<Item = Node<Ms>>,
172 NI: Iterator<Item = &'a mut Node<Ms>>,
173{
174 pub fn new(old_children_iter: OI, new_children_iter: NI) -> Self {
176 Self {
177 old_children_iter: old_children_iter.peekable(),
178 new_children_iter: new_children_iter.peekable(),
179 old_children: VecDeque::new(),
180 new_children: VecDeque::new(),
181 matching_child_old: None,
182 matching_child_new: None,
183 matching_key: None,
184 keyed_mode: false,
185 }
186 }
187
188 fn next_command(&mut self) -> Option<PatchCommand<'a, Ms>> {
190 if !self.keyed_mode {
191 return self.yield_keyless();
192 }
193 if self.matching_key.is_some() {
195 return self.yield_keyed();
196 }
197 if self.old_children_iter.peek().is_some() || self.new_children_iter.peek().is_some() {
199 self.matching_key = find_matching(
200 &mut self.old_children_iter,
201 &mut self.old_children,
202 &mut self.new_children_iter,
203 &mut self.new_children,
204 );
205 if self.matching_key.is_some() {
207 return self.yield_keyed();
208 }
209 }
210 self.yield_remaining()
211 }
212
213 fn yield_keyless(&mut self) -> Option<PatchCommand<'a, Ms>> {
217 let (child_old, child_new) = loop {
219 let old = self
223 .old_children
224 .pop_back()
225 .or_else(|| self.old_children_iter.next());
226 let new = self.new_children_iter.next();
227 if matches!((&old, &new), (Some(Node::Empty), Some(Node::Empty))) {
229 continue;
230 }
231 break (old, new);
232 };
233
234 match (child_old, child_new) {
235 (Some(child_old), Some(child_new)) => {
236 if child_old.el_key().is_none() && child_new.el_key().is_none() {
237 return self.patch_or_replace(child_old, child_new);
238 }
239
240 self.keyed_mode = true;
242
243 let key_old = PatchKey::new(&child_old);
244 let key_new = PatchKey::new(child_new);
245 if key_old == key_new {
246 self.matching_key = key_new;
247 }
248 if !child_old.is_empty() {
249 self.old_children.push_back(child_old);
250 }
251 if !child_new.is_empty() {
252 self.new_children.push_back(child_new);
253 }
254 self.next_command()
255 }
256 (None, Some(child_new)) => self.append(child_new),
257 (Some(child_old), None) => self.remove(child_old),
258 (None, None) => None,
259 }
260 }
261
262 fn yield_keyed(&mut self) -> Option<PatchCommand<'a, Ms>> {
268 match (
271 self.matching_child_old.as_ref(),
272 self.matching_child_new.as_ref(),
273 ) {
274 (None, None) => {
276 let child_old = self
279 .old_children
280 .pop_back()
281 .expect("old child from the queue");
282 let child_new = self
283 .new_children
284 .pop_back()
285 .expect("new child from the queue");
286
287 let key_old = PatchKey::new(&child_old);
288 let key_new = PatchKey::new(child_new);
289
290 if key_old == self.matching_key && key_new == self.matching_key {
291 self.matching_child_old = Some(child_old);
292 self.matching_child_new = Some(child_new);
293 return self.yield_keyed();
294 }
295 if key_old == self.matching_key {
296 let next_node = child_old.node_ws().unwrap().clone();
297 self.matching_child_old = Some(child_old);
298 return self.insert(child_new, next_node);
299 }
300 if key_new == self.matching_key {
301 self.matching_child_new = Some(child_new);
302 return self.remove(child_old);
303 }
304 self.patch_or_replace(child_old, child_new)
305 }
306 (Some(child_old), None) => {
308 let child_new = self
309 .new_children
310 .pop_back()
311 .expect("node with a matching key");
312
313 if PatchKey::new(child_new) == self.matching_key {
314 self.matching_child_new = Some(child_new);
315 return self.yield_keyed();
316 }
317 let next_node = child_old
318 .node_ws()
319 .expect("old node connected to web_sys node")
320 .clone();
321 self.insert(child_new, next_node)
322 }
323 (None, Some(_)) => {
325 let child_old = self
326 .old_children
327 .pop_back()
328 .expect("node with a matching key");
329
330 if PatchKey::new(&child_old) == self.matching_key {
331 self.matching_child_old = Some(child_old);
332 return self.yield_keyed();
333 }
334 self.remove(child_old)
335 }
336 (Some(_), Some(_)) => {
338 self.matching_key = None;
340 let child_old = self.matching_child_old.take().unwrap();
341 let child_new = self.matching_child_new.take().unwrap();
342 self.patch_or_replace(child_old, child_new)
343 }
344 }
345 }
346
347 fn yield_remaining(&mut self) -> Option<PatchCommand<'a, Ms>> {
349 match (self.old_children.pop_back(), self.new_children.pop_back()) {
350 (Some(child_old), Some(child_new)) => self.patch_or_replace(child_old, child_new),
351 (Some(child_old), None) => self.remove(child_old),
352 (None, Some(child_new)) => self.append(child_new),
353 (None, None) => None,
354 }
355 }
356
357 fn append(&mut self, child_new: &'a mut Node<Ms>) -> Option<PatchCommand<'a, Ms>> {
358 Some(match child_new {
359 Node::Element(el_new) => PatchCommand::AppendEl { el_new },
360 Node::Text(text_new) => PatchCommand::AppendText { text_new },
361 Node::Empty | Node::NoChange => return self.next_command(),
362 })
363 }
364
365 fn insert(
366 &mut self,
367 child_new: &'a mut Node<Ms>,
368 next_node: web_sys::Node,
369 ) -> Option<PatchCommand<'a, Ms>> {
370 Some(match child_new {
371 Node::Element(el_new) => PatchCommand::InsertEl { el_new, next_node },
372 Node::Text(text_new) => PatchCommand::InsertText {
373 text_new,
374 next_node,
375 },
376 Node::Empty | Node::NoChange => return self.next_command(),
377 })
378 }
379
380 #[allow(clippy::option_if_let_else)]
381 fn patch_or_replace(
382 &mut self,
383 child_old: Node<Ms>,
384 child_new: &'a mut Node<Ms>,
385 ) -> Option<PatchCommand<'a, Ms>> {
386 Some(match child_old {
387 Node::Element(el_old) => match child_new {
388 Node::Element(el_new) => {
389 if el_can_be_patched(&el_old, el_new) {
390 PatchCommand::PatchEl { el_old, el_new }
391 } else {
392 PatchCommand::ReplaceElByEl { el_old, el_new }
393 }
394 }
395 Node::Text(text_new) => PatchCommand::ReplaceElByText { el_old, text_new },
396 Node::Empty => PatchCommand::RemoveEl { el_old },
397 Node::NoChange => {
398 *child_new = Node::Element(el_old);
399 return self.next_command();
400 }
401 },
402 Node::Text(text_old) => match child_new {
403 Node::Element(el_new) => PatchCommand::ReplaceTextByEl { text_old, el_new },
404 Node::Text(text_new) => PatchCommand::PatchText { text_old, text_new },
405 Node::Empty => PatchCommand::RemoveText { text_old },
406 Node::NoChange => {
407 *child_new = Node::Text(text_old);
408 return self.next_command();
409 }
410 },
411 Node::Empty => match child_new {
412 Node::Element(el_new) => {
413 if let Some(next_node) =
414 find_next_node_ws(&mut self.old_children_iter, &mut self.old_children)
415 {
416 PatchCommand::InsertEl { el_new, next_node }
417 } else {
418 PatchCommand::AppendEl { el_new }
419 }
420 }
421 Node::Text(text_new) => {
422 if let Some(next_node) =
423 find_next_node_ws(&mut self.old_children_iter, &mut self.old_children)
424 {
425 PatchCommand::InsertText {
426 text_new,
427 next_node,
428 }
429 } else {
430 PatchCommand::AppendText { text_new }
431 }
432 }
433 Node::Empty => return self.next_command(),
434 Node::NoChange => {
435 *child_new = child_old;
436 return self.next_command();
437 }
438 },
439 Node::NoChange => panic!("Node::NoChange cannot be an old VDOM node!"),
440 })
441 }
442
443 fn remove(&mut self, child_old: Node<Ms>) -> Option<PatchCommand<'a, Ms>> {
444 Some(match child_old {
445 Node::Element(el_old) => PatchCommand::RemoveEl { el_old },
446 Node::Text(text_old) => PatchCommand::RemoveText { text_old },
447 Node::Empty | Node::NoChange => return self.next_command(),
448 })
449 }
450}
451
452impl<'a, Ms, OI, NI> Iterator for PatchGen<'a, Ms, OI, NI>
453where
454 Ms: 'static,
455 OI: Iterator<Item = Node<Ms>>,
456 NI: Iterator<Item = &'a mut Node<Ms>>,
457{
458 type Item = PatchCommand<'a, Ms>;
459
460 fn next(&mut self) -> Option<Self::Item> {
461 self.next_command()
462 }
463}
464
465pub fn el_can_be_patched<Ms>(el_old: &El<Ms>, el_new: &El<Ms>) -> bool {
467 el_old.namespace == el_new.namespace && el_old.tag == el_new.tag && el_old.key == el_new.key
468}
469
470fn find_matching<OI, NI, ON, NN, Ms>(
477 old_children_iter: &mut Peekable<OI>,
478 old_children: &mut VecDeque<ON>,
479 new_children_iter: &mut Peekable<NI>,
480 new_children: &mut VecDeque<NN>,
481) -> Option<PatchKey>
482where
483 OI: Iterator<Item = ON>,
484 NI: Iterator<Item = NN>,
485 ON: Borrow<Node<Ms>>,
486 NN: Borrow<Node<Ms>>,
487 Ms: 'static,
488{
489 let mut seen_old_keys: BTreeSet<_> = old_children
492 .iter()
493 .filter_map(|node| PatchKey::new(node.borrow()))
494 .collect();
495 let mut seen_new_keys: BTreeSet<_> = new_children
497 .iter()
498 .filter_map(|node| PatchKey::new(node.borrow()))
499 .collect();
500
501 while old_children_iter.peek().is_some() || new_children_iter.peek().is_some() {
502 let should_pick_old_child = old_children_iter.peek().is_some()
504 && (new_children_iter.peek().is_none() || new_children.len() > old_children.len());
505
506 if should_pick_old_child {
507 if let Some(key) = fetch_next_item(old_children_iter, old_children)
508 .and_then(|child| PatchKey::new(child.borrow()))
509 {
510 if seen_new_keys.contains(&key) {
511 return Some(key);
512 }
513 seen_old_keys.insert(key);
514 }
515 } else if new_children_iter.peek().is_some() {
516 if let Some(key) = fetch_next_item(new_children_iter, new_children)
517 .and_then(|child| PatchKey::new(child.borrow()))
518 {
519 if seen_old_keys.contains(&key) {
520 return Some(key);
521 }
522 seen_new_keys.insert(key);
523 }
524 }
525 }
526 None
527}
528
529fn find_next_node_ws<I, N, Ms>(
532 children_iter: &mut Peekable<I>,
533 children: &mut VecDeque<N>,
534) -> Option<web_sys::Node>
535where
536 I: Iterator<Item = N>,
537 N: Borrow<Node<Ms>>,
538 Ms: 'static,
539{
540 if let node_ws @ Some(_) = children.iter().find_map(|child| child.borrow().node_ws()) {
542 return node_ws.cloned();
543 }
544 while let Some(child) = fetch_next_item(children_iter, children) {
546 if let node_ws @ Some(_) = child.borrow().node_ws() {
547 return node_ws.cloned();
548 }
549 }
550 None
551}
552
553fn fetch_next_item<'a, I, T>(source_iter: &'a mut I, queue: &'a mut VecDeque<T>) -> Option<&'a T>
556where
557 I: Iterator<Item = T>,
558{
559 source_iter.next().and_then(move |item| {
560 queue.push_front(item);
561 queue.front()
562 })
563}