open_editor/
editor_call_builder.rs1use std::{
2 env::{self, temp_dir},
3 path::{Path, PathBuf},
4 process::{Command, Stdio},
5};
6
7use crate::{ENV_VARS, editor::Editor, editor_kind::EditorKind, errors::OpenEditorError};
8
9pub struct EditorCallBuilder {
10 editor: Option<Editor>,
11 file_path: Option<PathBuf>,
12 custom_env_vars: Vec<String>,
13 wait: bool,
14 line_number: usize,
15 column_number: usize,
16}
17impl Default for EditorCallBuilder {
18 fn default() -> Self {
19 Self {
20 editor: None,
21 file_path: None,
22 custom_env_vars: vec![],
23 wait: true,
24 line_number: 1,
25 column_number: 1,
26 }
27 }
28}
29
30impl EditorCallBuilder {
31 #[must_use]
39 pub fn new() -> Self {
40 Self::default()
41 }
42 #[must_use]
43 pub fn at_line(self, line: usize) -> Self {
45 Self {
46 line_number: line,
47 ..self
48 }
49 }
50 #[must_use]
51 pub fn at_column(self, line: usize) -> Self {
53 Self {
54 column_number: line,
55 ..self
56 }
57 }
58 #[must_use]
60 pub fn wait_for_editor(self, value: bool) -> Self {
61 Self {
62 wait: value,
63 ..self
64 }
65 }
66 #[must_use]
69 pub fn with_env_vars(self, env_vars: &[&str]) -> Self {
70 let mut custom_env_vars = self.custom_env_vars;
71 custom_env_vars.extend(env_vars.iter().map(|&s| s.to_string()));
72 Self {
73 custom_env_vars,
74 ..self
75 }
76 }
77 #[must_use]
79 pub fn with_editor(self, editor: Editor) -> Self {
80 Self {
81 editor: Some(editor),
82 ..self
83 }
84 }
85 pub fn open_editor(&self) -> Result<String, OpenEditorError> {
91 self.edit_string("")
92 }
93 pub fn edit_string_mut(&self, string: &mut String) -> Result<(), OpenEditorError> {
100 *string = self.edit_string(string)?;
101 Ok(())
102 }
103
104 pub fn edit_string(&self, string: &str) -> Result<String, OpenEditorError> {
110 let file_path = match &self.file_path {
111 Some(path) => path,
112 None => &{
113 let mut filename = temp_dir();
114 filename.push(String::from("open_editor_tmp_file"));
115 filename
116 },
117 };
118 std::fs::write(file_path, string).map_err(OpenEditorError::FileManipulationFail)?;
120 self.open_file(file_path)?;
121 let result =
122 std::fs::read_to_string(file_path).map_err(OpenEditorError::FileManipulationFail)?;
123
124 std::fs::remove_file(file_path).map_err(|_| {
126 OpenEditorError::TempFileCleanupFail(file_path.to_string_lossy().into_owned())
127 })?;
128
129 Ok(result)
130 }
131 pub fn open_file(&self, file_path: &Path) -> Result<(), OpenEditorError> {
136 let editor = match &self.editor {
137 Some(editor) => editor,
138 None => &self.get_default_editor()?,
139 };
140
141 let editor_call = EditorCall {
143 editor: editor.clone(),
144 file_path: file_path.to_path_buf(),
145 wait: self.wait,
146 line_number: self.line_number,
147 column_number: self.column_number,
148 };
149 editor_call.call()
150 }
151 fn get_default_editor(&self) -> Result<Editor, OpenEditorError> {
153 self.custom_env_vars
154 .clone()
155 .into_iter()
156 .chain(ENV_VARS.iter().map(|&s| s.to_string()))
157 .filter_map(env::var_os)
158 .filter(|var| !var.is_empty())
159 .map(|v| {
160 let path = Editor::get_full_path(v.clone());
161 (v.into_string().ok(), path)
162 })
163 .filter_map(|(v, path)| v.map(|v| (v, path)))
164 .map(|(v, cmd)| (Editor::new(EditorKind::from(v), cmd)))
165 .next()
166 .ok_or(OpenEditorError::NoEditorFound)
167 }
168}
169struct EditorCall {
171 editor: Editor,
172 file_path: PathBuf,
173 wait: bool,
174 line_number: usize,
175 column_number: usize,
176}
177impl EditorCall {
178 pub fn call(&self) -> Result<(), OpenEditorError> {
183 self.editor.validate_executable()?; let command = Command::new(&self.editor.binary_path)
185 .args(self.editor.editor_type.get_editor_args(
186 &self.file_path,
187 self.wait,
188 self.line_number,
189 self.column_number,
190 ))
191 .stdin(Stdio::inherit())
192 .stdout(Stdio::inherit())
193 .stderr(Stdio::inherit())
194 .spawn();
195
196 if !self.wait {
197 return Ok(());
198 }
199
200 match command {
201 Ok(output) => {
202 let output = output
203 .wait_with_output()
204 .map_err(|e| OpenEditorError::CommandFail { error: e })?;
205 if output.status.success() {
206 Ok(())
207 } else {
208 let stderr = String::from_utf8_lossy(&output.stderr);
209 Err(OpenEditorError::EditorCallError {
210 exit_code: output.status.code(),
211 stderr: stderr.to_string(),
212 })
213 }
214 }
215 Err(e) => Err(OpenEditorError::CommandFail { error: e }),
216 }
217 }
218}