use core::cell::RefCell;
use core::marker::PhantomData;
use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::vec::Vec;
use crate::Environment;
use crate::Mill;
use crate::MillOf;
use crate::Page;
use crate::PageOf;
use crate::Pages;
use crate::Setup;
use crate::Sketch;
use crate::Status;
use super::Ream;
struct ConcurrentCreate<S, I> {
input: I,
marker: PhantomData<fn() -> S>,
}
trait CreateItem<E>
where
E: Environment,
{
fn create(self, page: PageOf<E>) -> Box<dyn Sketch<Env = E>>;
fn create_from_box(self: Box<Self>, page: PageOf<E>) -> Box<dyn Sketch<Env = E>>;
}
impl<S, I> CreateItem<S::Env> for ConcurrentCreate<S, I>
where
S: Sketch + Setup<I> + 'static,
{
fn create(self, page: PageOf<S::Env>) -> Box<dyn Sketch<Env = S::Env>> {
Box::new(S::setup(page, self.input))
}
fn create_from_box(self: Box<Self>, page: PageOf<S::Env>) -> Box<dyn Sketch<Env = S::Env>> {
self.create(page)
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "ream-concurrent")))]
pub struct Concurrent<E> {
to_create_queue: Rc<RefCell<Vec<Box<dyn CreateItem<E>>>>>,
to_creates: Vec<Box<dyn CreateItem<E>>>,
sketches: Vec<Box<dyn Sketch<Env = E>>>,
}
impl<E> core::fmt::Debug for Concurrent<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Concurrent").finish()
}
}
impl<E> Default for Concurrent<E>
where
E: Environment,
{
fn default() -> Self {
Self {
sketches: Vec::new(),
to_creates: Vec::new(),
to_create_queue: Rc::new(RefCell::new(Vec::new())),
}
}
}
impl<E> Ream for Concurrent<E>
where
E: Environment,
{
type Env = E;
type Error = core::convert::Infallible;
fn update(&mut self, mill: &mut MillOf<Self::Env>) -> Result<Status, Self::Error> {
if !self.to_creates.is_empty() {
let to_creates = core::mem::take(&mut self.to_creates);
for to_create in to_creates {
self.sketches
.push(to_create.create_from_box(mill.new_page()));
}
}
if !self.to_create_queue.borrow().is_empty() {
let queue: Vec<Box<dyn CreateItem<E>>> =
core::mem::take(self.to_create_queue.borrow_mut().as_mut());
for to_create in queue {
self.sketches
.push(to_create.create_from_box(mill.new_page()));
}
}
for sketch in &mut self.sketches {
sketch.get_page_mut().start_group();
if let Some(event) = sketch.as_mut().get_page_mut().next_event_in_group() {
sketch.get_page_mut().before_event(&event);
sketch.handle_environment_event(&event);
sketch.get_page_mut().finish_event(event);
}
sketch.get_page_mut().end_group();
}
self.sketches
.retain(|sketch| sketch.get_page().status() == Status::Continue);
if self.sketches.is_empty() {
Ok(Status::Stop)
} else {
Ok(Status::Continue)
}
}
}
impl<E> Concurrent<E>
where
E: Environment,
{
pub fn create_proxy(&self) -> ConcurrentProxy<E> {
ConcurrentProxy {
to_create_queue: self.to_create_queue.clone(),
}
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "ream-concurrent")))]
pub struct ConcurrentProxy<E> {
to_create_queue: Rc<RefCell<Vec<Box<dyn CreateItem<E>>>>>,
}
impl<E> core::fmt::Debug for ConcurrentProxy<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ConcurrentProxy").finish()
}
}
impl<E> ConcurrentProxy<E>
where
E: Environment,
{
pub fn add_page<S>(&self)
where
S: Sketch<Env = E> + Setup<()> + 'static,
{
self.add_page_with::<S, ()>(())
}
pub fn add_page_with<S, I>(&self, input: I)
where
S: Sketch<Env = E> + Setup<I> + 'static,
I: 'static,
{
let mut queue = self.to_create_queue.borrow_mut();
queue.push(Box::new(ConcurrentCreate::<S, I> {
marker: PhantomData,
input,
}));
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "ream-concurrent")))]
pub trait ConcurrentBookExt<E>
where
E: Environment,
{
fn ream_proxy(&self) -> ConcurrentProxy<E>;
fn add_page<S>(&mut self)
where
S: Setup<()> + Sketch<Env = E> + 'static;
fn add_page_with<S, I>(&mut self, input: I)
where
S: Setup<I> + Sketch<Env = E> + 'static,
I: 'static;
}
impl<E> ConcurrentBookExt<E> for Pages<Concurrent<E>>
where
E: Environment,
{
fn ream_proxy(&self) -> ConcurrentProxy<E> {
self.ream.create_proxy()
}
fn add_page<S>(&mut self)
where
S: Setup<()> + Sketch<Env = E> + 'static,
{
self.add_page_with::<S, ()>(())
}
fn add_page_with<S, I>(&mut self, input: I)
where
S: Setup<I> + Sketch<Env = E> + 'static,
I: 'static,
{
self.ream
.to_creates
.push(Box::new(ConcurrentCreate::<S, I> {
marker: PhantomData,
input,
}));
}
}