async_ui_core 0.1.0

Shared code for Async UI
Documentation
use std::{
    cell::RefCell,
    collections::BTreeMap,
    future::Future,
    pin::Pin,
    rc::Rc,
    task::{Context, Poll},
};

use pin_project_lite::pin_project;

use crate::{backend::BackendTrait, context::ContextMap, position::PositionIndex};

use super::{VNode, VNodeTrait};

pub struct ConcreteNodeVNode<B: BackendTrait> {
    inside: RefCell<Inside<B>>,
    context: ContextMap,
}
struct Inside<B: BackendTrait> {
    node: RefNode<B>,
    children: BTreeMap<PositionIndex, B::Node>,
}

impl<B: BackendTrait> ConcreteNodeVNode<B> {
    pub fn new(node: RefNode<B>, context: ContextMap) -> Self {
        Self {
            inside: RefCell::new(Inside {
                node,
                children: BTreeMap::new(),
            }),
            context,
        }
    }
}
pub enum RefNode<B: BackendTrait> {
    Parent { parent: B::Node },
    Sibling { parent: B::Node, sibling: B::Node },
}

impl<B: BackendTrait> VNodeTrait<B> for ConcreteNodeVNode<B> {
    fn add_child_node(&self, mut node: <B as BackendTrait>::Node, position: PositionIndex) {
        let mut inside = self.inside.borrow_mut();
        let Inside {
            node: this_node,
            children: children_map,
        } = &mut *inside;
        let next_node = children_map
            .range(position.clone()..)
            .next()
            .map(|(_k, v)| v);
        match this_node {
            RefNode::Parent { parent } => {
                B::add_child_node(parent, &mut node, next_node);
            }
            RefNode::Sibling { parent, sibling } => {
                B::add_child_node(parent, &mut node, Some(next_node.unwrap_or(sibling)));
            }
        }
        children_map.insert(position, node);
    }

    fn del_child_node(&self, position: PositionIndex) -> B::Node {
        let mut inside = self.inside.borrow_mut();
        let mut removed = inside.children.remove(&position).unwrap();
        match &mut inside.node {
            RefNode::Parent { parent } => B::del_child_node(parent, &mut removed),
            RefNode::Sibling { parent, .. } => B::del_child_node(parent, &mut removed),
        }
        removed
    }

    fn get_context_map<'s>(&'s self) -> &'s ContextMap {
        &self.context
    }
}

enum WithConcreteNodeState<B: BackendTrait> {
    NotStarted { node: RefNode<B> },
    Started { vnode: Rc<VNode<B>> },
    Null,
}
pin_project! {
    pub struct WithConcreteNode<B, F>
    where
        F: Future,
        B: BackendTrait
    {
        #[pin]
        future: F,
        state: WithConcreteNodeState<B>
    }
}

impl<B, F> WithConcreteNode<B, F>
where
    F: Future,
    B: BackendTrait,
{
    pub fn new(future: F, node: RefNode<B>) -> Self {
        Self {
            future,
            state: WithConcreteNodeState::NotStarted { node },
        }
    }
}

impl<B, F> Future for WithConcreteNode<B, F>
where
    F: Future,
    B: BackendTrait,
{
    type Output = F::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.project();
        let vnk = B::get_vnode_key();
        let vnode = match std::mem::replace(this.state, WithConcreteNodeState::Null) {
            WithConcreteNodeState::NotStarted { node } => Rc::new(
                ConcreteNodeVNode::new(node, vnk.with(|vn| vn.get_context_map().to_owned())).into(),
            ),
            WithConcreteNodeState::Started { vnode } => vnode,
            _ => unreachable!(),
        };
        let res = vnk.set(&vnode, || this.future.poll(cx));
        *this.state = WithConcreteNodeState::Started { vnode };
        res
    }
}