use bumpalo::Bump;
use crate::innerlude::Listener;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Edit<'d> {
SetText { text: &'d str },
RemoveSelfAndNextSiblings {},
ReplaceWith,
SetAttribute { name: &'d str, value: &'d str },
RemoveAttribute { name: &'d str },
PushReverseChild { n: u32 },
PopPushChild { n: u32 },
Pop,
AppendChild,
CreateTextNode { text: &'d str },
CreateElement { tag_name: &'d str },
NewEventListener { event_type: &'d str, idx: CbIdx },
UpdateEventListener { event_type: &'d str, idx: CbIdx },
RemoveEventListener { event_type: &'d str },
CreateElementNs { tag_name: &'d str, ns: &'d str },
SaveChildrenToTemporaries { temp: u32, start: u32, end: u32 },
PushChild { n: u32 },
PushTemporary { temp: u32 },
InsertBefore,
PopPushReverseChild { n: u32 },
RemoveChild { n: u32 },
SetClass { class_name: &'d str },
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct CbIdx {
pub gi_id: usize,
pub gi_gen: u64,
pub listener_idx: usize,
}
impl CbIdx {
pub fn from_gi_index(index: generational_arena::Index, listener_idx: usize) -> Self {
let (gi_id, gi_gen) = index.into_raw_parts();
Self {
gi_id,
gi_gen,
listener_idx,
}
}
}
pub type EditList<'src> = Vec<Edit<'src>>;
pub struct EditMachine<'src> {
pub traversal: Traversal,
next_temporary: u32,
forcing_new_listeners: bool,
pub emitter: EditList<'src>,
}
impl<'b> EditMachine<'b> {
pub fn new(_bump: &'b Bump) -> Self {
Self {
traversal: Traversal::new(),
next_temporary: 0,
forcing_new_listeners: false,
emitter: EditList::default(),
}
}
pub fn go_down(&mut self) {
self.traversal.down();
}
pub fn go_down_to_child(&mut self, index: usize) {
self.traversal.down();
self.traversal.sibling(index);
}
pub fn go_down_to_reverse_child(&mut self, index: usize) {
self.traversal.down();
self.traversal.reverse_sibling(index);
}
pub fn go_up(&mut self) {
self.traversal.up();
}
pub fn go_to_sibling(&mut self, index: usize) {
self.traversal.sibling(index);
}
pub fn go_to_temp_sibling(&mut self, temp: u32) {
self.traversal.up();
self.traversal.down_to_temp(temp);
}
pub fn go_down_to_temp_child(&mut self, temp: u32) {
self.traversal.down_to_temp(temp);
}
pub fn commit_traversal(&mut self) {
if self.traversal.is_committed() {
log::debug!("Traversal already committed");
return;
}
for mv in self.traversal.commit() {
match mv {
MoveTo::Parent => {
log::debug!("emit: pop");
self.emitter.push(Edit::Pop {});
}
MoveTo::Child(n) => {
log::debug!("emit: push_child({})", n);
self.emitter.push(Edit::PushChild { n });
}
MoveTo::ReverseChild(n) => {
log::debug!("emit: push_reverse_child({})", n);
self.emitter.push(Edit::PushReverseChild { n });
}
MoveTo::Sibling(n) => {
log::debug!("emit: pop_push_child({})", n);
self.emitter.push(Edit::PopPushChild { n });
}
MoveTo::ReverseSibling(n) => {
log::debug!("emit: pop_push_reverse_child({})", n);
self.emitter.push(Edit::PopPushReverseChild { n });
}
MoveTo::TempChild(temp) => {
log::debug!("emit: push_temporary({})", temp);
self.emitter.push(Edit::PushTemporary { temp });
}
}
}
}
pub fn traversal_is_committed(&self) -> bool {
self.traversal.is_committed()
}
}
impl<'a> EditMachine<'a> {
pub fn next_temporary(&self) -> u32 {
self.next_temporary
}
pub fn set_next_temporary(&mut self, next_temporary: u32) {
self.next_temporary = next_temporary;
}
pub fn save_children_to_temporaries(&mut self, start: usize, end: usize) -> u32 {
debug_assert!(self.traversal_is_committed());
debug_assert!(start < end);
let temp_base = self.next_temporary;
self.next_temporary = temp_base + (end - start) as u32;
self.emitter.push(Edit::SaveChildrenToTemporaries {
temp: temp_base,
start: start as u32,
end: end as u32,
});
temp_base
}
pub fn push_temporary(&mut self, temp: u32) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::PushTemporary { temp });
}
pub fn remove_child(&mut self, child: usize) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::RemoveChild { n: child as u32 })
}
pub fn insert_before(&mut self) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::InsertBefore {})
}
pub fn ensure_string(&mut self, _string: &str) -> StringKey {
todo!()
}
pub fn set_text(&mut self, text: &'a str) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::SetText { text });
}
pub fn remove_self_and_next_siblings(&mut self) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::RemoveSelfAndNextSiblings {});
}
pub fn replace_with(&mut self) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::ReplaceWith {});
}
pub fn set_attribute(&mut self, name: &'a str, value: &'a str, is_namespaced: bool) {
debug_assert!(self.traversal_is_committed());
if name == "class" && !is_namespaced {
self.emitter.push(Edit::SetClass { class_name: value });
} else {
self.emitter.push(Edit::SetAttribute { name, value });
}
}
pub fn remove_attribute(&mut self, name: &'a str) {
self.emitter.push(Edit::RemoveAttribute { name });
}
pub fn append_child(&mut self) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::AppendChild {});
}
pub fn create_text_node(&mut self, text: &'a str) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::CreateTextNode { text });
}
pub fn create_element(&mut self, tag_name: &'a str) {
self.emitter.push(Edit::CreateElement { tag_name });
}
pub fn create_element_ns(&mut self, tag_name: &'a str, ns: &'a str) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::CreateElementNs { tag_name, ns });
}
pub fn push_force_new_listeners(&mut self) -> bool {
let old = self.forcing_new_listeners;
self.forcing_new_listeners = true;
old
}
pub fn pop_force_new_listeners(&mut self, previous: bool) {
debug_assert!(self.forcing_new_listeners);
self.forcing_new_listeners = previous;
}
pub fn new_event_listener(&mut self, event: &'a str, idx: CbIdx) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::NewEventListener {
event_type: event,
idx,
});
}
pub fn update_event_listener(&mut self, event: &'a str, idx: CbIdx) {
debug_assert!(self.traversal_is_committed());
if self.forcing_new_listeners {
self.new_event_listener(event, idx);
return;
}
self.emitter.push(Edit::NewEventListener {
event_type: event,
idx,
});
}
pub fn remove_event_listener(&mut self, event: &'a str) {
debug_assert!(self.traversal_is_committed());
self.emitter
.push(Edit::RemoveEventListener { event_type: event });
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MoveTo {
Parent,
Child(u32),
ReverseChild(u32),
Sibling(u32),
ReverseSibling(u32),
TempChild(u32),
}
#[derive(Debug)]
pub struct Traversal {
uncommitted: Vec<MoveTo>,
}
impl Traversal {
pub fn new() -> Traversal {
Traversal {
uncommitted: Vec::with_capacity(32),
}
}
pub fn up(&mut self) {
match self.uncommitted.last() {
Some(MoveTo::Sibling(_)) | Some(MoveTo::ReverseSibling(_)) => {
self.uncommitted.pop();
self.uncommitted.push(MoveTo::Parent);
}
Some(MoveTo::TempChild(_)) | Some(MoveTo::Child(_)) | Some(MoveTo::ReverseChild(_)) => {
self.uncommitted.pop();
}
_ => {
self.uncommitted.push(MoveTo::Parent);
}
}
}
pub fn down(&mut self) {
if let Some(&MoveTo::Parent) = self.uncommitted.last() {
self.uncommitted.pop();
self.sibling(0);
} else {
self.uncommitted.push(MoveTo::Child(0));
}
}
pub fn sibling(&mut self, index: usize) {
let index = index as u32;
match self.uncommitted.last_mut() {
Some(MoveTo::Sibling(ref mut n)) | Some(MoveTo::Child(ref mut n)) => {
*n = index;
}
Some(MoveTo::ReverseSibling(_)) => {
self.uncommitted.pop();
self.uncommitted.push(MoveTo::Sibling(index));
}
Some(MoveTo::TempChild(_)) | Some(MoveTo::ReverseChild(_)) => {
self.uncommitted.pop();
self.uncommitted.push(MoveTo::Child(index))
}
_ => {
self.uncommitted.push(MoveTo::Sibling(index));
}
}
}
pub fn reverse_sibling(&mut self, index: usize) {
let index = index as u32;
match self.uncommitted.last_mut() {
Some(MoveTo::ReverseSibling(ref mut n)) | Some(MoveTo::ReverseChild(ref mut n)) => {
*n = index;
}
Some(MoveTo::Sibling(_)) => {
self.uncommitted.pop();
self.uncommitted.push(MoveTo::ReverseSibling(index));
}
Some(MoveTo::TempChild(_)) | Some(MoveTo::Child(_)) => {
self.uncommitted.pop();
self.uncommitted.push(MoveTo::ReverseChild(index))
}
_ => {
self.uncommitted.push(MoveTo::ReverseSibling(index));
}
}
}
pub fn down_to_temp(&mut self, temp: u32) {
match self.uncommitted.last() {
Some(MoveTo::Sibling(_)) | Some(MoveTo::ReverseSibling(_)) => {
self.uncommitted.pop();
}
Some(MoveTo::Parent)
| Some(MoveTo::TempChild(_))
| Some(MoveTo::Child(_))
| Some(MoveTo::ReverseChild(_))
| None => {
}
}
self.uncommitted.push(MoveTo::TempChild(temp));
}
#[inline]
pub fn is_committed(&self) -> bool {
self.uncommitted.is_empty()
}
#[inline]
pub fn commit(&mut self) -> Moves {
Moves {
inner: self.uncommitted.drain(..),
}
}
#[inline]
pub fn reset(&mut self) {
self.uncommitted.clear();
}
}
pub struct Moves<'a> {
inner: std::vec::Drain<'a, MoveTo>,
}
impl Iterator for Moves<'_> {
type Item = MoveTo;
#[inline]
fn next(&mut self) -> Option<MoveTo> {
self.inner.next()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_traversal() {
fn t<F>(f: F) -> Box<dyn FnMut(&mut Traversal)>
where
F: 'static + FnMut(&mut Traversal),
{
Box::new(f) as _
}
for (mut traverse, expected_moves) in vec![
(
t(|t| {
t.down();
}),
vec![MoveTo::Child(0)],
),
(
t(|t| {
t.up();
}),
vec![MoveTo::Parent],
),
(
t(|t| {
t.sibling(42);
}),
vec![MoveTo::Sibling(42)],
),
(
t(|t| {
t.down();
t.up();
}),
vec![],
),
(
t(|t| {
t.down();
t.sibling(2);
t.up();
}),
vec![],
),
(
t(|t| {
t.down();
t.sibling(3);
}),
vec![MoveTo::Child(3)],
),
(
t(|t| {
t.down();
t.sibling(4);
t.sibling(8);
}),
vec![MoveTo::Child(8)],
),
(
t(|t| {
t.sibling(1);
t.sibling(1);
}),
vec![MoveTo::Sibling(1)],
),
(
t(|t| {
t.reverse_sibling(3);
}),
vec![MoveTo::ReverseSibling(3)],
),
(
t(|t| {
t.down();
t.reverse_sibling(3);
}),
vec![MoveTo::ReverseChild(3)],
),
(
t(|t| {
t.down();
t.reverse_sibling(3);
t.up();
}),
vec![],
),
(
t(|t| {
t.down();
t.reverse_sibling(3);
t.reverse_sibling(6);
}),
vec![MoveTo::ReverseChild(6)],
),
(
t(|t| {
t.up();
t.reverse_sibling(3);
t.reverse_sibling(6);
}),
vec![MoveTo::Parent, MoveTo::ReverseSibling(6)],
),
(
t(|t| {
t.up();
t.sibling(3);
t.sibling(6);
}),
vec![MoveTo::Parent, MoveTo::Sibling(6)],
),
(
t(|t| {
t.sibling(3);
t.sibling(6);
t.up();
}),
vec![MoveTo::Parent],
),
(
t(|t| {
t.reverse_sibling(3);
t.reverse_sibling(6);
t.up();
}),
vec![MoveTo::Parent],
),
(
t(|t| {
t.down();
t.down_to_temp(3);
}),
vec![MoveTo::Child(0), MoveTo::TempChild(3)],
),
(
t(|t| {
t.down_to_temp(3);
t.sibling(5);
}),
vec![MoveTo::Child(5)],
),
(
t(|t| {
t.down_to_temp(3);
t.reverse_sibling(5);
}),
vec![MoveTo::ReverseChild(5)],
),
(
t(|t| {
t.down_to_temp(3);
t.up();
}),
vec![],
),
(
t(|t| {
t.sibling(2);
t.up();
t.down_to_temp(3);
}),
vec![MoveTo::Parent, MoveTo::TempChild(3)],
),
(
t(|t| {
t.up();
t.down_to_temp(3);
}),
vec![MoveTo::Parent, MoveTo::TempChild(3)],
),
] {
let mut traversal = Traversal::new();
traverse(&mut traversal);
let actual_moves: Vec<_> = traversal.commit().collect();
assert_eq!(actual_moves, expected_moves);
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StringKey(u32);
impl From<StringKey> for u32 {
#[inline]
fn from(key: StringKey) -> u32 {
key.0
}
}