sequent_repl/commands/
event_proxy.rs1use 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
13pub 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
63pub 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 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;