mod effect;
mod request;
mod resolve;
use std::collections::VecDeque;
use std::sync::{Mutex, RwLock};
pub use effect::{Effect, EffectFFI};
pub use request::Request;
pub use resolve::{RequestHandle, Resolvable, ResolveError};
use crate::{App, Command};
pub struct Core<A>
where
A: App,
{
model: RwLock<A::Model>,
app: A,
root_command: Mutex<Command<A::Effect, A::Event>>,
}
impl<A> Core<A>
where
A: App + Default,
A::Model: Default,
{
#[must_use]
pub fn new() -> Self {
Self {
model: RwLock::default(),
app: A::default(),
root_command: Mutex::new(Command::done()),
}
}
}
impl<A> Core<A>
where
A: App,
{
#[must_use]
pub fn new_with(app: A, model: A::Model) -> Self {
Self {
model: RwLock::new(model),
app,
root_command: Mutex::new(Command::done()),
}
}
pub fn process_event(&self, event: A::Event) -> Vec<A::Effect> {
let mut model = self.model.write().expect("Model RwLock was poisoned.");
let command = self.app.update(event, &mut model);
drop(model);
let mut root_command = self
.root_command
.lock()
.expect("Capability runtime lock was poisoned");
root_command.spawn(|ctx| command.into_future(ctx));
drop(root_command);
self.process()
}
pub fn resolve<Output>(
&self,
request: &mut impl Resolvable<Output>,
result: Output,
) -> Result<Vec<A::Effect>, ResolveError>
{
let resolve_result = request.resolve(result);
debug_assert!(resolve_result.is_ok());
resolve_result?;
Ok(self.process())
}
pub(crate) fn process(&self) -> Vec<A::Effect> {
let mut root_command = self
.root_command
.lock()
.expect("Capability runtime lock was poisoned");
let mut events: VecDeque<_> = root_command.events().collect();
while let Some(event_from_commands) = events.pop_front() {
let mut model = self.model.write().expect("Model RwLock was poisoned.");
let command = self.app.update(event_from_commands, &mut model);
drop(model);
root_command.spawn(|ctx| command.into_future(ctx));
events.extend(root_command.events());
}
root_command.effects().collect()
}
pub fn view(&self) -> A::ViewModel {
let model = self.model.read().expect("Model RwLock was poisoned.");
self.app.view(&model)
}
}
impl<A> Default for Core<A>
where
A: App + Default,
A::Model: Default,
{
fn default() -> Self {
Self::new()
}
}