use {
crate::{
app::SelectionType,
keys,
command::Sequence,
errors::ConfError,
verb::*,
},
serde::Deserialize,
std::convert::TryFrom,
};
#[derive(Default, Debug, Clone, Deserialize)]
pub struct VerbConf {
invocation: Option<String>,
internal: Option<String>,
external: Option<ExecPattern>,
execution: Option<ExecPattern>,
cmd: Option<String>,
cmd_separator: Option<String>,
key: Option<String>,
#[serde(default)]
keys: Vec<String>,
shortcut: Option<String>,
leave_broot: Option<bool>,
from_shell: Option<bool>,
apply_to: Option<String>,
set_working_dir: Option<bool>,
description: Option<String>,
}
impl TryFrom<&VerbConf> for Verb {
type Error = ConfError;
fn try_from(vc: &VerbConf) -> Result<Self, Self::Error> {
if vc.leave_broot == Some(false) && vc.from_shell == Some(true) {
return Err(ConfError::InvalidVerbConf {
details: "You can't simultaneously have leave_broot=false and from_shell=true".to_string(),
});
}
let invocation = vc.invocation.clone().filter(|i| !i.is_empty());
let internal = vc.internal.as_ref().filter(|i| !i.is_empty());
let external = vc.external.as_ref().filter(|i| !i.is_empty());
let cmd = vc.cmd.as_ref().filter(|i| !i.is_empty());
let cmd_separator = vc.cmd_separator.as_ref().filter(|i| !i.is_empty());
let execution = vc.execution.as_ref().filter(|i| !i.is_empty());
let make_external_execution = |s| {
ExternalExecution::new(
s,
ExternalExecutionMode::from_conf(vc.from_shell, vc.leave_broot),
)
.with_set_working_dir(vc.set_working_dir)
};
let execution = match (execution, internal, external, cmd) {
(Some(ep), None, None, None) => {
if let Some(internal_pattern) = ep.as_internal_pattern() {
VerbExecution::Internal(InternalExecution::try_from(internal_pattern)?)
} else {
VerbExecution::External(make_external_execution(ep.clone()))
}
}
(None, Some(s), None, None) => {
VerbExecution::Internal(if s.starts_with(':') || s.starts_with(' ') {
InternalExecution::try_from(&s[1..])?
} else {
InternalExecution::try_from(&s)?
})
}
(None, None, Some(ep), None) => {
VerbExecution::External(make_external_execution(ep.clone()))
}
(None, None, None, Some(s)) => VerbExecution::Sequence(SequenceExecution {
sequence: Sequence::new(s, cmd_separator),
}),
_ => {
return Err(ConfError::InvalidVerbConf {
details: "You must define either internal, external or cmd".to_string(),
});
}
};
let description = vc
.description
.clone()
.map(VerbDescription::from_text)
.unwrap_or_else(|| VerbDescription::from_code(execution.to_string()));
let mut verb = Verb::new(
invocation.as_deref(),
execution,
description,
)?;
let mut unchecked_keys = vc.keys.clone();
if let Some(key) = &vc.key {
unchecked_keys.push(key.clone());
}
let mut checked_keys = Vec::new();
for key in &unchecked_keys {
let key = keys::parse_key(key)?;
if keys::is_reserved(key) {
return Err(ConfError::ReservedKey {
key: keys::key_event_desc(key),
});
}
checked_keys.push(key);
}
if !checked_keys.is_empty() {
verb.add_keys(checked_keys);
}
if let Some(shortcut) = &vc.shortcut {
verb.names.push(shortcut.clone());
}
verb.selection_condition = match vc.apply_to.as_deref() {
Some("file") => SelectionType::File,
Some("directory") => SelectionType::Directory,
Some("any") => SelectionType::Any,
None => SelectionType::Any,
Some(s) => {
return Err(ConfError::InvalidVerbConf {
details: format!("{:?} isn't a valid value of apply_to", s),
});
}
};
Ok(verb)
}
}