topola/src/interactor/interactor.rs

142 lines
4.1 KiB
Rust

// SPDX-FileCopyrightText: 2024 Topola contributors
//
// SPDX-License-Identifier: MIT
use std::ops::ControlFlow;
use spade::InsertionError;
use crate::{
autorouter::{
execution::Command,
history::History,
invoker::{Invoker, InvokerError},
Autorouter,
},
board::{AccessMesadata, Board},
interactor::{
activity::{
ActivityContext, ActivityError, ActivityStepperWithStatus, InteractiveEvent,
InteractiveInput,
},
interaction::InteractionStepper,
},
stepper::{Abort, OnEvent, Step},
};
/// Structure that manages the invoker and activities
pub struct Interactor<M> {
invoker: Invoker<M>,
activity: Option<ActivityStepperWithStatus>,
}
impl<M: AccessMesadata> Interactor<M> {
/// Create a new instance of Interactor with the given Board instance
pub fn new(board: Board<M>) -> Result<Self, InsertionError> {
Ok(Self {
invoker: Invoker::new(Autorouter::new(board)?),
activity: None,
})
}
/// Execute a command
pub fn execute(&mut self, command: Command) -> Result<(), InvokerError> {
self.invoker.execute(command)
}
/// Start executing an activity
pub fn schedule(&mut self, command: Command) -> Result<(), InvokerError> {
self.activity = Some(ActivityStepperWithStatus::new_execution(
self.invoker.execute_stepper(command)?,
));
Ok(())
}
/// Start an interaction activity
pub fn interact(&mut self, interaction: InteractionStepper) {
self.activity = Some(ActivityStepperWithStatus::new_interaction(interaction));
}
/// Undo last command
pub fn undo(&mut self) -> Result<(), InvokerError> {
self.invoker.undo()
}
/// Redo last command
pub fn redo(&mut self) -> Result<(), InvokerError> {
self.invoker.redo()
}
/// Abort the currently running execution or activity
pub fn abort(&mut self) {
if let Some(ref mut activity) = self.activity.take() {
activity.abort(&mut self.invoker);
}
}
/// Replay last command
pub fn replay(&mut self, history: History) {
self.invoker.replay(history);
}
/// Update the currently running execution or activity, given an event
pub fn update_for_event(
&mut self,
interactive_input: &InteractiveInput,
interactive_event: InteractiveEvent,
) -> ControlFlow<Result<(), ActivityError>> {
if let Some(ref mut activity) = self.activity {
match activity.on_event(
&mut ActivityContext {
interactive_input,
invoker: &mut self.invoker,
},
interactive_event,
) {
Ok(()) => ControlFlow::Continue(()),
Err(err) => {
self.activity = None;
ControlFlow::Break(Err(err.into()))
}
}
} else {
ControlFlow::Break(Ok(()))
}
}
/// Update the currently running execution or activity
pub fn update(
&mut self,
interactive_input: &InteractiveInput,
) -> ControlFlow<Result<(), ActivityError>> {
if let Some(ref mut activity) = self.activity {
match activity.step(&mut ActivityContext {
interactive_input,
invoker: &mut self.invoker,
}) {
Ok(ControlFlow::Continue(())) => ControlFlow::Continue(()),
Ok(ControlFlow::Break(_msg)) => {
self.activity = None;
ControlFlow::Break(Ok(()))
}
Err(err) => {
self.activity = None;
ControlFlow::Break(Err(err))
}
}
} else {
ControlFlow::Break(Ok(()))
}
}
/// Returns the invoker
pub fn invoker(&self) -> &Invoker<M> {
&self.invoker
}
/// Returns the currently running activity
pub fn maybe_activity(&self) -> &Option<ActivityStepperWithStatus> {
&self.activity
}
}