1use std::{
2 env::var,
3 error,
4 fs::File,
5 io::{self, Read, Write},
6 process::{Command, ExitStatus},
7};
8use tempfile::NamedTempFile;
9
10#[derive(Debug)]
11pub enum EditorInputError {
12 ExitStatus(ExitStatus),
13 IO(io::Error),
14}
15
16impl std::fmt::Display for EditorInputError {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 match self {
19 EditorInputError::ExitStatus(ex) => {
20 write!(f, "Editor exited with unsuccessful exit status {}", ex)
21 }
22 EditorInputError::IO(io) => io.fmt(f),
23 }
24 }
25}
26
27impl error::Error for EditorInputError {
28 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
29 match *self {
30 EditorInputError::ExitStatus(_) => None,
31 EditorInputError::IO(ref e) => Some(e),
32 }
33 }
34}
35
36impl From<io::Error> for EditorInputError {
37 fn from(e: io::Error) -> Self {
38 Self::IO(e)
39 }
40}
41
42pub fn input_from_editor(placeholder: &str) -> Result<String, EditorInputError> {
50 let editor = var("EDITOR").unwrap_or("vi".to_string());
51
52 let mut tmpfile = NamedTempFile::new()?;
53 tmpfile.write_all(placeholder.as_bytes())?;
54 let file_path = tmpfile.into_temp_path();
55
56 let status = Command::new(editor).arg(&file_path).status()?;
57 if !status.success() {
58 return Err(EditorInputError::ExitStatus(status));
59 }
60
61 let mut editable = String::new();
62 File::open(file_path)?.read_to_string(&mut editable)?;
63
64 Ok(editable)
65}