open_editor/
editor_call_builder.rs1use std::{
2 env,
3 ffi::OsString,
4 path::{Path, PathBuf},
5 process::{Command, Stdio},
6};
7
8use crate::{editor::Editor, editor_kind::EditorKind, errors::OpenEditorError};
9
10static ENV_VARS: &[&str] = &["VISUAL", "EDITOR"];
11
12pub struct EditorCallBuilder {
13 editor: Editor,
14 file_path: PathBuf,
15 wait: bool,
16 line_number: usize,
17 column_number: usize,
18}
19
20impl EditorCallBuilder {
21 pub fn new<P: AsRef<Path>>(file_path: P) -> Result<Self, OpenEditorError> {
28 Ok(Self {
29 editor: Self::get_default_editor()?,
30 file_path: file_path.as_ref().to_path_buf(),
31 wait: true,
32 line_number: 1,
33 column_number: 1,
34 })
35 }
36 #[must_use]
37 pub fn at_line(self, line: usize) -> Self {
39 Self {
40 line_number: line,
41 ..self
42 }
43 }
44 #[must_use]
45 pub fn at_column(self, line: usize) -> Self {
47 Self {
48 column_number: line,
49 ..self
50 }
51 }
52 #[must_use]
54 pub fn wait_for_editor(self, value: bool) -> Self {
55 Self {
56 wait: value,
57 ..self
58 }
59 }
60 pub fn call_editor(&self) -> Result<(), OpenEditorError> {
65 self.editor.validate_executable()?;
66 let command = Command::new(&self.editor.binary_path)
67 .args(self.editor.editor_type.get_editor_args(
68 &self.file_path,
69 self.wait,
70 self.line_number,
71 self.column_number,
72 ))
73 .stdin(Stdio::inherit())
74 .stdout(Stdio::inherit())
75 .stderr(Stdio::inherit())
76 .spawn();
77
78 if !self.wait {
79 return Ok(());
80 }
81
82 match command {
83 Ok(output) => {
84 let output = output
85 .wait_with_output()
86 .map_err(|e| OpenEditorError::CommandFail { error: e })?;
87 if output.status.success() {
88 Ok(())
89 } else {
90 let stderr = String::from_utf8_lossy(&output.stderr);
91 Err(OpenEditorError::EditorCallError {
92 exit_code: output.status.code(),
93 stderr: stderr.to_string(),
94 })
95 }
96 }
97 Err(e) => Err(OpenEditorError::CommandFail { error: e }),
98 }
99 }
100 fn get_full_path(editor_name: OsString) -> PathBuf {
102 match which::which(editor_name.clone()) {
103 Ok(path) => path,
104 Err(_) => PathBuf::from(editor_name), }
106 }
107 fn get_default_editor() -> Result<Editor, OpenEditorError> {
109 ENV_VARS
110 .iter()
111 .filter_map(env::var_os)
112 .filter(|var| !var.is_empty())
113 .map(|v| {
114 let path = EditorCallBuilder::get_full_path(v.clone());
115 (v.into_string().ok(), path)
116 })
117 .filter_map(|(v, path)| v.map(|v| (v, path)))
118 .map(|(v, cmd)| (Editor::new(EditorKind::from(v), cmd)))
119 .next()
120 .ok_or(OpenEditorError::NoEditorFound)
121 }
122}