1#![forbid(unsafe_code)]
2
3use std::borrow::Cow;
4use std::convert::AsRef;
5use core::fmt;
6use std::env::{self, VarError};
7use std::process::{Command, ExitStatus};
8
9pub fn get_editor(override_editor: Option<Cow<str>>) -> Result<Cow<str>, VarError> {
12 if let Some(z) = override_editor {
13 return Ok(z);
14 }
15
16 match env::var("VISUAL") {
17 Ok(result) => return Ok(result.into()),
18 Err(VarError::NotPresent) => {},
19 Err(error) => return Err(error),
20 }
21
22 match env::var("EDITOR") {
23 Ok(result) => return Ok(result.into()),
24 Err(VarError::NotPresent) => {},
25 Err(error) => return Err(error),
26 }
27
28 Ok("vi".into())
29}
30
31#[derive(Debug)]
32pub enum SEError {
33 Process(std::io::Error),
34 Var(VarError),
35}
36
37impl std::error::Error for SEError {
38 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
39 match self {
40 SEError::Process(source) => Some(&*source),
41 SEError::Var(source) => Some(&*source),
42 }
43 }
44}
45
46impl fmt::Display for SEError {
47 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
48 match self {
49 SEError::Process(_) => {
50 formatter.write_str("editor spawning/waiting failed")
51 }
52 SEError::Var(_) => {
53 formatter.write_str("got invalid environment variable")
54 }
55 }
56 }
57}
58
59type SEResult = Result<ExitStatus, SEError>;
60
61pub fn spawn_editor(override_editor: Option<&str>, extra_args: &[&str]) -> SEResult {
70 let editor: std::borrow::Cow<str> = get_editor(override_editor.map(Into::into)).map_err(SEError::Var)?;
71
72 Ok(Command::new(&*editor)
73 .args(extra_args)
74 .spawn()
75 .and_then(|mut c| c.wait())
76 .map_err(SEError::Process)?)
77}
78
79pub fn spawn_editor_generic<Ta, Tb>(override_editor: Option<Ta>, extra_args: &[Tb]) -> SEResult
82where
83 Ta: AsRef<str>,
84 Tb: AsRef<str>,
85{
86 let real_oore = override_editor.as_ref().map(|x| x.as_ref());
87 let xar: Vec<_> = extra_args.iter().map(|x| x.as_ref()).collect();
88 spawn_editor(real_oore, &xar[..])
89}
90
91#[inline]
99pub fn spawn_editor_with_args<Tb: AsRef<str>>(extra_args: &[Tb]) -> SEResult {
100 spawn_editor_generic::<&str, Tb>(None, extra_args)
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 mod default_editor {
109 use std::env;
110
111 fn it_falls_back_to_vi() {
112 env::remove_var("VISUAL");
113 env::remove_var("EDITOR");
114
115 assert_eq!(crate::get_editor(None), Ok("vi".into()));
116 }
117
118 fn it_returns_visual() {
119 env::set_var("VISUAL", "test1");
120 env::remove_var("EDITOR");
121
122 assert_eq!(crate::get_editor(None), Ok("test1".to_string().into()));
123 }
124
125 fn it_returns_editor() {
126 env::remove_var("VISUAL");
127 env::set_var("EDITOR", "test2");
128
129 assert_eq!(crate::get_editor(None), Ok("test2".to_string().into()));
130 }
131
132 fn it_returns_visual_before_editor() {
133 env::set_var("VISUAL", "test3");
134 env::set_var("EDITOR", "test4");
135
136 assert_eq!(crate::get_editor(None), Ok("test3".to_string().into()));
137 }
138
139 #[test]
140 fn all_tests() {
141 it_falls_back_to_vi();
143 it_returns_visual();
144 it_returns_editor();
145 it_returns_visual_before_editor();
146 }
147 }
148
149 #[test]
150 #[ignore]
151 fn testit() {
152 let _ = spawn_editor_with_args(&["src/lib.rs"]);
153 }
154}