1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use {
super::*,
crate::{
app::*,
display::W,
errors::ProgramError,
launchable::Launchable,
path,
},
std::{
fs::OpenOptions,
io::Write,
},
};
/// Definition of how the user input should be interpreted
/// to be executed in an external command.
#[derive(Debug, Clone)]
pub struct ExternalExecution {
/// the pattern which will result in an executable string when
/// completed with the args.
/// This pattern may include names coming from the invocation
/// pattern (like {my-arg}) and special names automatically filled by
/// broot from the selection and application state:
/// * {file}
/// * {directory}
/// * {parent}
/// * {other-panel-file}
/// * {other-panel-directory}
/// * {other-panel-parent}
pub exec_pattern: ExecPattern,
/// how the external process must be launched
pub exec_mode: ExternalExecutionMode,
/// whether the working dir of the external process must be set
/// to the current directory
pub set_working_dir: bool,
}
impl ExternalExecution {
pub fn new(
exec_pattern: ExecPattern,
exec_mode: ExternalExecutionMode,
) -> Self {
Self {
exec_pattern,
exec_mode,
set_working_dir: false,
}
}
pub fn with_set_working_dir(mut self, b: Option<bool>) -> Self {
if let Some(b) = b {
self.set_working_dir = b;
}
self
}
pub fn to_cmd_result(
&self,
w: &mut W,
builder: ExecutionStringBuilder<'_>,
con: &AppContext,
) -> Result<AppStateCmdResult, ProgramError> {
if self.exec_mode.is_from_shell() {
self.exec_from_shell_cmd_result(builder, con)
} else {
self.exec_cmd_result(w, builder, con)
}
}
/// build the cmd result as an executable which will be called from shell
fn exec_from_shell_cmd_result(
&self,
builder: ExecutionStringBuilder<'_>,
con: &AppContext,
) -> Result<AppStateCmdResult, ProgramError> {
if let Some(ref export_path) = con.launch_args.cmd_export_path {
// Broot was probably launched as br.
// the whole command is exported in the passed file
let f = OpenOptions::new().append(true).open(export_path)?;
writeln!(&f, "{}", builder.shell_exec_string(&self.exec_pattern))?;
Ok(AppStateCmdResult::Quit)
} else if let Some(ref export_path) = con.launch_args.file_export_path {
// old version of the br function: only the file is exported
// in the passed file
let f = OpenOptions::new().append(true).open(export_path)?;
writeln!(&f, "{}", builder.sel.path.to_string_lossy())?;
Ok(AppStateCmdResult::Quit)
} else {
Ok(AppStateCmdResult::DisplayError(
"this verb needs broot to be launched as `br`. Try `broot --install` if necessary."
.to_string(),
))
}
}
/// build the cmd result as an executable which will be called in a process
/// launched by broot
fn exec_cmd_result(
&self,
w: &mut W,
builder: ExecutionStringBuilder<'_>,
con: &AppContext,
) -> Result<AppStateCmdResult, ProgramError> {
let launchable = Launchable::program(
builder.exec_token(&self.exec_pattern),
if self.set_working_dir {
Some(path::closest_dir(builder.sel.path))
} else {
None
},
con,
)?;
if self.exec_mode.is_leave_broot() {
Ok(AppStateCmdResult::from(launchable))
} else {
info!("Executing not leaving, launchable {:?}", launchable);
let execution = launchable.execute(Some(w));
match execution {
Ok(()) => {
debug!("ok");
Ok(AppStateCmdResult::RefreshState { clear_cache: true })
}
Err(e) => {
warn!("launchable failed : {:?}", e);
Ok(AppStateCmdResult::DisplayError(e.to_string()))
}
}
}
}
}