use crate::ScopeId;
use crate::Task;
use crate::VirtualDom;
use crate::innerlude::Effect;
use std::hash::Hash;
#[derive(Debug, Clone, Copy, Eq)]
pub struct ScopeOrder {
pub(crate) height: u32,
pub(crate) id: ScopeId,
}
impl ScopeOrder {
pub fn new(height: u32, id: ScopeId) -> Self {
Self { height, id }
}
}
impl PartialEq for ScopeOrder {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl PartialOrd for ScopeOrder {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ScopeOrder {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.height.cmp(&other.height).then(self.id.cmp(&other.id))
}
}
impl Hash for ScopeOrder {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl VirtualDom {
fn remove_empty_dirty_tasks(&mut self) {
let mut dirty_tasks = self.runtime.dirty_tasks.borrow_mut();
while dirty_tasks
.first_key_value()
.is_some_and(|(_, tasks)| tasks.is_empty())
{
dirty_tasks.pop_first();
}
}
fn scope_order(&self, id: ScopeId) -> Option<ScopeOrder> {
self.runtime
.try_get_state(id)
.map(|scope| ScopeOrder::new(scope.height(), id))
}
pub(crate) fn queue_task(&mut self, task: Task, order: ScopeOrder) {
let mut dirty_tasks = self.runtime.dirty_tasks.borrow_mut();
let tasks = dirty_tasks.entry(order).or_default();
if !tasks.contains(&task) {
tasks.push_back(task);
}
}
pub(crate) fn queue_scope(&mut self, id: ScopeId) -> bool {
let Some(order) = self.scope_order(id) else {
return false;
};
self.dirty_scopes.insert(order)
}
pub(crate) fn mark_clean(&mut self, id: ScopeId) -> bool {
self.scope_order(id)
.is_some_and(|order| self.dirty_scopes.remove(&order))
}
pub(crate) fn pop_dirty_descendant_scope(&mut self, ancestor: ScopeId) -> Option<ScopeOrder> {
let order = self
.dirty_scopes
.iter()
.find(|order| self.runtime.is_descendant_of(order.id, ancestor))
.copied()?;
self.dirty_scopes.take(&order)
}
pub(crate) fn has_dirty_scopes(&self) -> bool {
!self.dirty_scopes.is_empty()
}
pub(crate) fn pop_task(&mut self) -> Option<Task> {
self.remove_empty_dirty_tasks();
let mut dirty_tasks = self.runtime.dirty_tasks.borrow_mut();
let mut entry = dirty_tasks.first_entry()?;
let order = *entry.key();
debug_assert!(self.scopes.contains(order.id.index()));
let tasks = entry.get_mut();
let task = tasks.pop_front()?;
if tasks.is_empty() {
entry.remove_entry();
}
Some(task)
}
pub(crate) fn pop_effect(&mut self) -> Option<Effect> {
let mut pending_effects = self.runtime.pending_effects.borrow_mut();
let effect = pending_effects.pop_first()?;
debug_assert!(self.scopes.contains(effect.order.id.index()));
Some(effect)
}
pub(crate) fn pop_work(&mut self) -> Option<Work> {
let dirty_scope = self.dirty_scopes.first().copied();
#[cfg(debug_assertions)]
if let Some(scope) = dirty_scope.as_ref() {
assert!(self.scopes.contains(scope.id.index()));
}
self.remove_empty_dirty_tasks();
let dirty_task = self
.runtime
.dirty_tasks
.borrow()
.first_key_value()
.map(|(order, _)| *order);
match (dirty_scope, dirty_task) {
(Some(scope), Some(task)) => match scope.cmp(&task) {
std::cmp::Ordering::Less => {
let scope = self.dirty_scopes.pop_first().unwrap();
Some(Work::RerunScope(scope))
}
std::cmp::Ordering::Equal | std::cmp::Ordering::Greater => {
Some(Work::PollTask(self.pop_task().unwrap()))
}
},
(Some(_), None) => {
let scope = self.dirty_scopes.pop_first().unwrap();
Some(Work::RerunScope(scope))
}
(None, Some(_)) => Some(Work::PollTask(self.pop_task().unwrap())),
(None, None) => None,
}
}
}
#[derive(Debug)]
pub(crate) enum Work {
RerunScope(ScopeOrder),
PollTask(Task),
}