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
10pub struct EditorCallBuilder {
11 editor: Editor,
12 file_path: PathBuf,
13 wait: bool,
14 line_number: usize,
15 column_number: usize,
16}
17
18impl EditorCallBuilder {
19 pub fn new<P: AsRef<Path>>(file_path: P) -> Result<Self, OpenEditorError> {
29 Self::new_with_env_vars(file_path, super::ENV_VARS)
30 }
31 pub fn new_with_env_vars<P: AsRef<Path>>(
34 file_path: P,
35 env_vars: &[&str],
36 ) -> Result<Self, OpenEditorError> {
37 Ok(Self {
38 editor: Self::get_default_editor(env_vars)?,
39 file_path: file_path.as_ref().to_path_buf(),
40 wait: true,
41 line_number: 1,
42 column_number: 1,
43 })
44 }
45 #[must_use]
46 pub fn at_line(self, line: usize) -> Self {
48 Self {
49 line_number: line,
50 ..self
51 }
52 }
53 #[must_use]
54 pub fn at_column(self, line: usize) -> Self {
56 Self {
57 column_number: line,
58 ..self
59 }
60 }
61 #[must_use]
63 pub fn wait_for_editor(self, value: bool) -> Self {
64 Self {
65 wait: value,
66 ..self
67 }
68 }
69 pub fn call_editor(&self) -> Result<(), OpenEditorError> {
74 self.editor.validate_executable()?;
75 let command = Command::new(&self.editor.binary_path)
76 .args(self.editor.editor_type.get_editor_args(
77 &self.file_path,
78 self.wait,
79 self.line_number,
80 self.column_number,
81 ))
82 .stdin(Stdio::inherit())
83 .stdout(Stdio::inherit())
84 .stderr(Stdio::inherit())
85 .spawn();
86
87 if !self.wait {
88 return Ok(());
89 }
90
91 match command {
92 Ok(output) => {
93 let output = output
94 .wait_with_output()
95 .map_err(|e| OpenEditorError::CommandFail { error: e })?;
96 if output.status.success() {
97 Ok(())
98 } else {
99 let stderr = String::from_utf8_lossy(&output.stderr);
100 Err(OpenEditorError::EditorCallError {
101 exit_code: output.status.code(),
102 stderr: stderr.to_string(),
103 })
104 }
105 }
106 Err(e) => Err(OpenEditorError::CommandFail { error: e }),
107 }
108 }
109 fn get_full_path(editor_name: OsString) -> PathBuf {
111 match which::which(editor_name.clone()) {
112 Ok(path) => path,
113 Err(_) => PathBuf::from(editor_name), }
115 }
116 fn get_default_editor(env_vars: &[&str]) -> Result<Editor, OpenEditorError> {
118 env_vars
119 .iter()
120 .filter_map(env::var_os)
121 .filter(|var| !var.is_empty())
122 .map(|v| {
123 let path = EditorCallBuilder::get_full_path(v.clone());
124 (v.into_string().ok(), path)
125 })
126 .filter_map(|(v, path)| v.map(|v| (v, path)))
127 .map(|(v, cmd)| (Editor::new(EditorKind::from(v), cmd)))
128 .next()
129 .ok_or(OpenEditorError::NoEditorFound)
130 }
131}