sequent_repl/commands/
save.rs1use crate::commands::prompt::YesNo;
4use crate::Context;
5use sequent::persistence::yaml;
6use sequent::SimulationError;
7use revolver::command::{
8 ApplyCommandError, ApplyOutcome, Command, Description, Example, NamedCommandParser,
9 ParseCommandError,
10};
11use revolver::looper::Looper;
12use revolver::terminal::Terminal;
13use serde::ser::Serialize;
14use std::borrow::Cow;
15use std::marker::PhantomData;
16use std::path::PathBuf;
17
18pub struct Save<S, C> {
21 path: String,
22 __phantom_data: PhantomData<(S, C)>
23}
24
25impl<S, C> Save<S, C> {
26 pub fn new(path: String) -> Self {
27 Self {
28 path,
29 __phantom_data: PhantomData::default()
30 }
31 }
32}
33
34impl<S: Clone + Serialize, C: Context<State = S>, T: Terminal> Command<T> for Save<S, C> {
35 type Context = C;
36 type Error = SimulationError<S>;
37
38 fn apply(
39 &mut self,
40 looper: &mut Looper<C, SimulationError<S>, T>,
41 ) -> Result<ApplyOutcome, ApplyCommandError<SimulationError<S>>> {
42 let path = PathBuf::from(&self.path);
43 if path.exists() {
44 let response = looper
45 .terminal()
46 .read_from_str_default("Output file exists. Overwrite? [y/N]: ")?;
47
48 if let YesNo::No = response {
49 return Ok(ApplyOutcome::Skipped);
50 }
51 }
52 yaml::write_to_file(looper.context().sim().scenario(), path)
53 .map_err(SimulationError::from)
54 .map_err(ApplyCommandError::Application)?;
55
56 looper
57 .terminal()
58 .print_line(&format!("Saved scenario to '{}'.", self.path))?;
59 Ok(ApplyOutcome::Applied)
60 }
61}
62
63pub struct Parser<S, C> {
65 __phantom_data: PhantomData<(S, C)>
66}
67
68impl<S, C> Default for Parser<S, C> {
69 fn default() -> Self {
70 Self {
71 __phantom_data: PhantomData::default()
72 }
73 }
74}
75
76impl<S: Clone + Serialize + 'static, C: Context<State = S> + 'static, T: Terminal> NamedCommandParser<T>
77 for Parser<S, C>
78{
79 type Context = C;
80 type Error = SimulationError<S>;
81
82 fn parse(
83 &self,
84 s: &str,
85 ) -> Result<Box<dyn Command<T, Context = C, Error = SimulationError<S>>>, ParseCommandError> {
86 if s.is_empty() {
87 return Err(ParseCommandError("empty arguments to 'save'".into()));
88 }
89 let path = s.into();
90 Ok(Box::new(Save::new(path)))
91 }
92
93 fn shorthand(&self) -> Option<Cow<'static, str>> {
94 None
95 }
96
97 fn name(&self) -> Cow<'static, str> {
98 "save".into()
99 }
100
101 fn description(&self) -> Description {
102 Description {
103 purpose: "Saves the current scenario to a file.".into(),
104 usage: "<path>".into(),
105 examples: vec![Example {
106 scenario: "save to a file named 'trixie.yaml' in the working directory".into(),
107 command: "trixie.yaml".into(),
108 }],
109 }
110 }
111}
112
113#[cfg(test)]
114mod tests;