use rmut_core::command;
use rmut_core::config::Config;
use crate::{ComposeBase, ComposeKind, Request, Session};
#[derive(Default)]
pub struct CommandRun {
pub reports: Vec<String>,
pub warnings: Vec<String>,
}
impl Session {
pub fn run_command_line(&mut self, line: &str) -> CommandRun {
let mut run = CommandRun::default();
let commands = match command::parse(line) {
Ok(commands) => commands,
Err(err) => {
self.error(err);
return run;
}
};
if commands.is_empty() {
return run;
}
let sort_before = (
self.config.index.sort.clone(),
self.config.index.sort_aux.clone(),
);
for cmd in commands {
let outcome = match &cmd {
command::Command::Bind { .. }
| command::Command::Macro { .. }
| command::Command::Push(_)
| command::Command::Exec(_) => {
self.requests.push(Request::Command(cmd));
continue;
}
command::Command::Alias { nick, expansion } => self.alias_command(nick, expansion),
command::Command::Unalias(nicks) => self.unalias_command(nicks),
command::Command::Unmailboxes(_) => {
let outcome = command::apply(&mut self.config, &cmd);
self.requests.push(Request::MailboxesChanged);
outcome
}
config_command => command::apply(&mut self.config, config_command),
};
match outcome {
Ok(Some(text)) => run.reports.push(text),
Ok(None) => {}
Err(err) => {
self.error(err);
return run;
}
}
}
self.config.expand_folders();
run.warnings = self.recompile();
if sort_before
!= (
self.config.index.sort.clone(),
self.config.index.sort_aux.clone(),
)
{
if let Some(spec) = self.config.index.sort.clone()
&& let Some((sort, rev)) = crate::parse_sort(&spec)
{
self.sort = sort;
self.sort_rev = rev;
}
self.apply_sort();
}
self.requests.push(Request::ConfigChanged);
run
}
fn alias_command(&mut self, nick: &str, expansion: &str) -> Result<Option<String>, String> {
if nick.contains(char::is_whitespace) {
return Err("the alias nick must be one word".into());
}
match rmut_core::alias::append_to(self.config.mail.alias_file.as_deref(), nick, expansion) {
Ok(_) => Ok(Some(format!("added: alias {nick} {expansion}"))),
Err(err) => Err(format!("cannot save the alias: {err:#}")),
}
}
fn unalias_command(&mut self, nicks: &[String]) -> Result<Option<String>, String> {
match rmut_core::alias::remove_from(self.config.mail.alias_file.as_deref(), nicks) {
Ok(0) => Ok(Some("no such alias".into())),
Ok(n) => Ok(Some(format!("removed {n} alias(es)"))),
Err(err) => Err(format!("cannot rewrite the alias file: {err:#}")),
}
}
fn run_hook(&mut self, what: &str, line: &str) {
self.clear_notice();
let run = self.run_command_line(line);
if let Some(err) = self.notice().filter(|n| n.is_error()).map(|n| n.text()) {
self.error(format!("{what}: {err}"));
} else if !run.warnings.is_empty() {
self.error(format!("{what}: {}", run.warnings.join("; ")));
}
}
pub fn run_folder_hooks(&mut self) {
if self.config.folder_hooks.is_empty() {
return;
}
let title = self.title.clone();
let lines: Vec<String> = self
.config
.folder_hooks
.iter()
.filter(|h| rmut_core::config::glob_match(&h.folder, &title))
.map(|h| h.command.clone())
.collect();
for line in lines {
self.run_hook("folder-hook", &line);
}
}
pub fn sync_message_hooks(&mut self) {
if self.message_hooks.is_empty() && self.active_message_hooks.is_empty() {
return;
}
let matching = self.matching_message_hooks();
if matching == self.active_message_hooks {
return;
}
self.active_message_hooks = matching.clone();
self.restore_hook_base();
if matching.is_empty() {
return;
}
self.hook_base = Some(Box::new(self.config.clone()));
for i in matching {
let Some(line) = self.message_hooks.get(i).map(|h| h.value.clone()) else {
continue;
};
self.run_hook("message-hook", &line);
}
}
pub fn clear_message_hooks(&mut self) {
self.active_message_hooks.clear();
self.restore_hook_base();
}
fn restore_hook_base(&mut self) {
let Some(base) = self.hook_base.take() else {
return;
};
self.config = *base;
let warnings = self.recompile();
if !warnings.is_empty() {
self.error(warnings.join("; "));
}
self.requests.push(Request::ConfigChanged);
}
pub fn apply_reply_hooks(
&mut self,
base: Option<&ComposeBase>,
kind: ComposeKind,
) -> Option<Box<Config>> {
if self.reply_hooks.is_empty()
|| !matches!(
kind,
ComposeKind::Reply | ComposeKind::GroupReply | ComposeKind::ListReply
)
{
return None;
}
let lines = self.reply_hook_lines(&base?.path);
if lines.is_empty() {
return None;
}
let saved = Box::new(self.config.clone());
for line in lines {
self.run_hook("reply-hook", &line);
}
Some(saved)
}
pub fn restore_after_reply_hooks(&mut self, saved: Option<Box<Config>>) {
let Some(saved) = saved else { return };
self.config = *saved;
let warnings = self.recompile();
if !warnings.is_empty() {
self.error(warnings.join("; "));
}
self.requests.push(Request::ConfigChanged);
}
}