158 lines
4.1 KiB
Rust
158 lines
4.1 KiB
Rust
use super::{
|
|
Command, CommandInfo, CommandParent, CommandRef, CommandStatus, GetCommandInfo, WithCommandInfo,
|
|
};
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
pub enum CommandResult {
|
|
Continue,
|
|
Done,
|
|
Cancel,
|
|
}
|
|
|
|
pub struct CommandContext<T>
|
|
where
|
|
T: CommandImp,
|
|
{
|
|
info: CommandInfo,
|
|
status: CommandStatus,
|
|
parent: Option<*mut CommandParent>,
|
|
state: <T as CommandImp>::State,
|
|
}
|
|
|
|
impl<T: CommandImp> CommandContext<T> {
|
|
pub fn status(&self) -> CommandStatus {
|
|
self.status
|
|
}
|
|
|
|
pub fn state(&mut self) -> &mut T::State {
|
|
&mut self.state
|
|
}
|
|
|
|
pub fn parent(&self) -> &mut CommandParent {
|
|
self.try_parent()
|
|
.expect("CommandContext parent called but no parent exists")
|
|
}
|
|
|
|
pub fn try_parent<'a>(&'a self) -> Option<&'a mut CommandParent> {
|
|
self.parent
|
|
.map(|ptr| unsafe { &mut *ptr } as &'a mut CommandParent)
|
|
}
|
|
}
|
|
|
|
impl<T: CommandImp> WithCommandInfo for CommandContext<T> {
|
|
fn command_info(&self) -> &CommandInfo {
|
|
&self.info
|
|
}
|
|
}
|
|
|
|
pub trait CommandImp {
|
|
type State: Default;
|
|
|
|
fn init(&self, ctx: &mut CommandContext<Self>)
|
|
where
|
|
Self: Sized;
|
|
fn step(&self, ctx: &mut CommandContext<Self>) -> CommandResult
|
|
where
|
|
Self: Sized;
|
|
fn finish(&self, ctx: &mut CommandContext<Self>)
|
|
where
|
|
Self: Sized;
|
|
}
|
|
|
|
pub struct GenericCommand<T: CommandImp> {
|
|
context: CommandContext<T>,
|
|
pub imp: T,
|
|
}
|
|
|
|
impl<T: CommandImp + 'static> GenericCommand<T> {
|
|
pub fn new(info: CommandInfo, imp: T) -> Self {
|
|
Self {
|
|
context: CommandContext {
|
|
info,
|
|
status: CommandStatus::Stopped,
|
|
parent: None,
|
|
state: T::State::default(),
|
|
},
|
|
imp,
|
|
}
|
|
}
|
|
|
|
pub fn new_ref(info: CommandInfo, imp: T) -> CommandRef<Self> {
|
|
Rc::new(RefCell::new(Self::new(info, imp)))
|
|
}
|
|
}
|
|
|
|
impl<T: CommandImp> WithCommandInfo for GenericCommand<T> {
|
|
fn command_info(&self) -> &CommandInfo {
|
|
&self.context.info
|
|
}
|
|
}
|
|
|
|
impl<T: CommandImp> Command for GenericCommand<T> {
|
|
fn status(&self) -> CommandStatus {
|
|
self.context.status
|
|
}
|
|
|
|
fn execute(&mut self, parent: &mut CommandParent) -> CommandStatus {
|
|
use self::CommandResult::*;
|
|
use self::CommandStatus::*;
|
|
let parent_ptr: *mut CommandParent = unsafe { std::mem::transmute(parent) };
|
|
self.context.parent = Some(parent_ptr);
|
|
let do_cancel = |me: &mut Self| {
|
|
me.context.status = Cancelling;
|
|
me.imp.finish(&mut me.context);
|
|
Cancelled
|
|
};
|
|
let do_step = |me: &mut Self| {
|
|
let result = me.imp.step(&mut me.context);
|
|
match result {
|
|
Continue => Running,
|
|
Done => {
|
|
me.imp.finish(&mut me.context);
|
|
Ended
|
|
}
|
|
Cancel => do_cancel(me),
|
|
}
|
|
};
|
|
let new_status = match self.status() {
|
|
Stopped => {
|
|
self.imp.init(&mut self.context);
|
|
do_step(self)
|
|
}
|
|
Running => do_step(self),
|
|
Ended => Ended,
|
|
Cancelling => do_cancel(self),
|
|
Cancelled => Cancelled,
|
|
};
|
|
self.context.status = new_status;
|
|
self.context.parent = None;
|
|
new_status
|
|
}
|
|
|
|
fn reset(&mut self) {
|
|
use self::CommandStatus::*;
|
|
self.context.status = match self.context.status {
|
|
Stopped | Ended | Cancelled => Stopped,
|
|
other => {
|
|
warn!(
|
|
"GenericCommand({}) reset called in non-resetable status {:?}",
|
|
self.name(),
|
|
other
|
|
);
|
|
self.imp.finish(&mut self.context);
|
|
Stopped
|
|
}
|
|
};
|
|
}
|
|
|
|
fn cancel(&mut self) {
|
|
use self::CommandStatus::*;
|
|
self.context.status = match self.context.status {
|
|
Running => Cancelling,
|
|
other => other,
|
|
};
|
|
}
|
|
}
|