use std::process::{Command, Stdio}; use std::io::{Read, Write, Seek, SeekFrom}; use tempfile::NamedTempFile; use crate::error::{Error::*, Result}; /// Launches the specified editor editing a temporary file and returns the /// contents written by the user. /// /// If prev_contents is Some(s) then the temporary file is started with those /// contents. pub fn get_string(note_editor: Option<&str>, prev_contents: Option) -> Result { let note_editor = if let Some(note_editor) = note_editor { note_editor.to_owned() } else if let Ok(editor_env) = std::env::var("EDITOR") { editor_env } else { return Err(EditorIsEmpty); }; let parts: Vec<_> = note_editor.split(' ').filter(|p| !p.is_empty()).collect(); let editor = if let Some(name) = parts.get(0) { name.to_owned() } else { return Err(EditorIsEmpty); }; let mut c = Command::new(editor); if parts.len() > 1 { for part in &parts[1..] { c.arg(part); } } let mut tmpfile = NamedTempFile::new().map_err(|e| NoTmpFile(e.to_string()))?; if let Some(contents) = prev_contents { tmpfile.write_all(contents.as_bytes())?; tmpfile.seek(SeekFrom::Start(0))?; } c.arg(tmpfile.as_ref()); let status = c .stdin(Stdio::inherit()) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .output().map_err(|e| EditorFailed { editor: note_editor.clone(), error: e.to_string(), })? .status; if status.success() { let mut note = String::new(); tmpfile.read_to_string(&mut note).map_err(|e| CouldntReadTmpFile(e.to_string()))?; Ok(note.trim().to_owned()) } else { Err(EditorFailed { editor: note_editor.clone(), error: status.to_string() }) } }