sequent_repl/commands/
event_proxy.rs

1//! Evaluation of a specific event.
2
3use crate::commands::prompt::YesNo;
4use crate::Context;
5use sequent::{Event, SimulationError, StaticNamed};
6use revolver::command::{ApplyCommandError, ApplyOutcome, Command, Description, NamedCommandParser, ParseCommandError};
7use revolver::looper::Looper;
8use revolver::terminal::{Terminal};
9use std::borrow::Cow;
10use std::marker::PhantomData;
11use std::str::FromStr;
12
13/// Command that delegates its evaluation to that of an [`Event`] object.
14pub struct EventProxy<S, C> {
15    event: Option<Box<dyn Event<State = S>>>,
16    __phantom_data: PhantomData<C>
17}
18
19impl<S, C> EventProxy<S, C> {
20    pub fn new(event: Option<Box<dyn Event<State = S>>>) -> Self {
21        Self {
22            event,
23            __phantom_data: PhantomData::default(),
24        }
25    }
26}
27
28impl<S, C: Context<State = S>, T: Terminal> Command<T> for EventProxy<S, C> {
29    type Context = C;
30    type Error = SimulationError<S>;
31
32    fn apply(&mut self, looper: &mut Looper<C, SimulationError<S>, T>) -> Result<ApplyOutcome, ApplyCommandError<SimulationError<S>>> {
33        let mut event = self.event.take().unwrap();
34        loop {
35            let result = looper.context().sim().push_event(event);
36            match result {
37                Ok(_) => {
38                    let (terminal, _, context) = looper.split();
39                    context.sim().step().map_err(ApplyCommandError::Application)?;
40                    context.print_state(terminal)?;
41                    return Ok(ApplyOutcome::Applied);
42                }
43                Err(SimulationError::TruncationRequired(rejected)) => {
44                    event = rejected;
45                    let response = looper.terminal().read_from_str_default(
46                        "Inserting into the timeline requires truncation. Continue? [y/N]: ",
47                    )?;
48                    match response {
49                        YesNo::Yes => {
50                            looper.context().sim().truncate();
51                        }
52                        YesNo::No => {
53                            return Ok(ApplyOutcome::Skipped);
54                        }
55                    }
56                }
57                Err(_) => unreachable!()
58            }
59        }
60    }
61}
62
63/// Parser for [`EventProxy`] of some event type.
64pub struct Parser<S, C, E: Event<State = S>> {
65    shorthand: Option<Cow<'static, str>>,
66    name: &'static str,
67    description: Description,
68    __phantom_data: PhantomData<(S, C, E)>,
69}
70
71impl<S, C, E> Parser<S, C, E>
72    where
73        E: Event<State = S> + FromStr + StaticNamed,
74{
75    /// Creates a new parser. The name is taken from the event type, provided the latter implements
76    /// [`StaticNamed`].
77    pub fn new(shorthand: Option<Cow<'static, str>>, description: Description) -> Self {
78        Self {
79            shorthand,
80            name: <E as StaticNamed>::name(),
81            description,
82            __phantom_data: PhantomData::default(),
83        }
84    }
85}
86
87impl<S: 'static, C: Context<State = S> + 'static, E, T: Terminal> NamedCommandParser<T> for Parser<S, C, E>
88    where
89        E: FromStr + Event<State = S> + 'static,
90        E::Err: ToString
91{
92    type Context = C;
93    type Error = SimulationError<S>;
94
95    fn parse(&self, s: &str) -> Result<Box<dyn Command<T, Context = C, Error = SimulationError<S>>>, ParseCommandError> {
96        let event = E::from_str(s).map_err(ParseCommandError::convert)?;
97        let proxy = EventProxy::new(Some(Box::new(event)));
98        Ok(Box::new(proxy))
99    }
100
101    fn shorthand(&self) -> Option<Cow<'static, str>> {
102        self.shorthand.clone()
103    }
104
105    fn name(&self) -> Cow<'static, str> {
106        self.name.into()
107    }
108
109    fn description(&self) -> Description {
110        self.description.clone()
111    }
112}
113
114#[cfg(test)]
115mod tests;