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 where T: CommandImp, { info: CommandInfo, status: CommandStatus, parent: Option<*mut CommandParent>, state: ::State, } impl CommandContext { 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 WithCommandInfo for CommandContext { fn command_info(&self) -> &CommandInfo { &self.info } } pub trait CommandImp { type State: Default; fn init(&self, ctx: &mut CommandContext) where Self: Sized; fn step(&self, ctx: &mut CommandContext) -> CommandResult where Self: Sized; fn finish(&self, ctx: &mut CommandContext) where Self: Sized; } pub struct GenericCommand { context: CommandContext, pub imp: T, } impl GenericCommand { 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 { Rc::new(RefCell::new(Self::new(info, imp))) } } impl WithCommandInfo for GenericCommand { fn command_info(&self) -> &CommandInfo { &self.context.info } } impl Command for GenericCommand { 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, }; } }