add_ed/io/
fake_io.rs

1use crate::{
2  io::IO,
3  ui::UILock,
4  buffer::iters::LinesIter,
5};
6use super::Result;
7
8#[derive(Debug, PartialEq)]
9pub enum FakeIOError {
10  ChildExitError,
11  NotFound,
12}
13impl std::fmt::Display for FakeIOError {
14  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
15    use FakeIOError::*;
16    match self {
17      ChildExitError => write!(f,"Child process returned error after running."),
18      NotFound => write!(f,"Could not open file. Not found or invalid path."),
19    }
20  }
21}
22impl std::error::Error for FakeIOError {}
23impl crate::error::IOErrorTrait for FakeIOError {}
24
25use std::collections::HashMap;
26
27#[derive(PartialEq, Eq, Hash, Clone)]
28pub struct ShellCommand {
29  pub command: String,
30  pub input: String,
31}
32
33/// An [`IO`] implementation intended to simulate filesystem and shell
34/// interactions for testing.
35#[derive(Clone)]
36pub struct FakeIO {
37  pub fake_fs: HashMap<String, String>,
38  pub fake_shell: HashMap<ShellCommand, String>,
39}
40
41impl IO for FakeIO {
42  /// Returns [`FakeIOError::ChildExitError`] if command is not represented by a
43  /// [`ShellCommand`] with empty input in `fake_shell`.
44  fn run_command(&mut self,
45    _ui: &mut UILock,
46    command: String,
47  ) -> Result<()> {
48    if self.fake_shell.contains_key(
49      &ShellCommand{command, input: String::new()}
50    ) {
51      Ok(())
52    } else {
53      // sh is child and returns error on command not found
54      Err(FakeIOError::ChildExitError.into())
55    }
56  }
57  /// Returns [`FakeIOError::ChildExitError`] if command is not represented by a
58  /// [`ShellCommand`] with empty input in `fake_shell`.
59  fn run_read_command(&mut self,
60    _ui: &mut UILock,
61    command: String,
62  ) -> Result<String> {
63    match self.fake_shell.get(
64      &ShellCommand{command, input: String::new()}
65    ) {
66      Some(x) => Ok(x.to_owned()),
67      // sh is child and returns error on command not found
68      None => Err(FakeIOError::ChildExitError.into()),
69    }
70  }
71  /// Returns [`FakeIOError::ChildExitError`] if command is not represented by a
72  /// [`ShellCommand`] with the given input in `fake_shell`.
73  fn run_write_command(&mut self,
74    _ui: &mut UILock,
75    command: String,
76    input: LinesIter,
77  ) -> Result<usize> {
78    let input = input.fold(String::new(), |mut s, x| {s.push_str(x); s});
79    let inputlen = input.len();
80    match self.fake_shell.get(
81      &ShellCommand{command, input}
82    ) {
83      Some(_) => Ok(inputlen),
84      // sh is child and returns error on command not found
85      None => Err(FakeIOError::ChildExitError.into()),
86    }
87  }
88  /// Returns [`FakeIOError::ChildExitError`] if command is not represented by a
89  /// [`ShellCommand`] with the given input in `fake_shell`.
90  fn run_transform_command(&mut self,
91    _ui: &mut UILock,
92    command: String,
93    input: LinesIter,
94  ) -> Result<String> {
95    let input = input.fold(String::new(), |mut s, x| {s.push_str(x); s});
96    match self.fake_shell.get(
97      &ShellCommand{command, input}
98    ) {
99      Some(x) => Ok(x.to_owned()),
100      // sh is child and returns error on command not found
101      None => Err(FakeIOError::ChildExitError.into()),
102    }
103  }
104  fn write_file(&mut self,
105    path: &str,
106    append: bool,
107    data: LinesIter,
108  ) -> Result<usize> {
109    let base_data = if append {
110      match self.fake_fs.get(path) {
111        Some(x) => x.clone(),
112        None => String::new(),
113      }
114    } else {
115      String::new()
116    };
117    let data = data.fold(base_data, |mut s, x|{s.push_str(x); s});
118    let datalen = data.len();
119    self.fake_fs.insert(path.to_owned(), data);
120    Ok(datalen)
121  }
122  /// Returns [`FakeIOError::NotFound`] if `fake_fs` doesn't have an entry with
123  /// the given path as key.
124  fn read_file(&mut self,
125    path: &str,
126    must_exist: bool,
127  ) -> Result<String> {
128    match self.fake_fs.get(path) {
129      Some(x) => Ok(x.to_owned()),
130      None => if must_exist {
131        Err(FakeIOError::NotFound.into())
132      } else {
133        Ok(String::new())
134      },
135    }
136  }
137}