use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::io::{BufRead, BufReader};
use anyhow::{Result, anyhow};
use constcat::concat;
use duct::cmd;
use mail_parser::MessageParser;
use regex::Regex;
use crate::model::Message;
pub const NEOMUTT_XDG_CONFIG_DIR: &str = ".config/neomutt";
const RC: &str = concat!(NEOMUTT_XDG_CONFIG_DIR, "/neomuttrc");
pub struct Mua;
impl Mua {
pub fn search(msg: &mut Message) -> Result<(String, String, String)> {
let (rc, account) = Self::account(&msg.sender_addr)?;
let maildirs = Self::maildirs(&account)?;
let (path, raw) = Self::draft(maildirs, &msg.id)?;
msg.raw = raw;
Ok((rc, account, path))
}
pub fn account(from: &String) -> Result<(String, String)> {
let path = format!("{}/{}", env::var("HOME")?, NEOMUTT_XDG_CONFIG_DIR);
let rc = String::from(format!("{}/{}", env::var("HOME")?, RC));
let account = String::from(cmd!("rg", "-l", "-g", "!tmp", from, &path).read()?);
Ok((rc, account))
}
fn maildirs(account: &String) -> Result<Vec<String>> {
let re = Regex::new(r#"set (folder|postponed) = "([^"]*)""#)?;
let file = File::open(account)?;
let reader = BufReader::new(file);
let mut map: HashMap<String, String> = HashMap::new();
for line in reader.lines() {
let line = line?;
if let Some(caps) = re.captures(&line) {
map.insert(caps[1].to_string(), caps[2].to_string());
}
}
let folder = match map.get("folder") {
Some(folder) => folder.replace(|c: char| c == '~', env!("HOME")),
None => {
return Err(anyhow!(
"You must specify the top-level maildir in your account file"
));
}
};
let postponed = match map.get("postponed") {
Some(postponed) => postponed.replace(|c: char| !c.is_alphanumeric(), ""),
None => {
return Err(anyhow!(
"You must specify the postponed maildir in your account file"
));
}
};
Ok(vec![
String::from(format!("{}/{}/new", folder, postponed)),
String::from(format!("{}/{}/cur", folder, postponed)),
String::from(format!("{}/{}/tmp", folder, postponed)),
])
}
fn draft(maildirs: Vec<String>, id: &String) -> Result<(String, String)> {
let mut path = String::new();
let mut raw = String::new();
for maildir in maildirs {
let drafts = fs::read_dir(maildir)?;
for draft in drafts {
let draft = draft?;
let msg = fs::read_to_string(draft.path())?;
let msg = match MessageParser::default().parse(&msg) {
Some(msg) => msg,
None => continue,
};
let draftid = match msg.message_id() {
Some(id) => id.to_owned(),
None => continue,
};
if draftid == *id {
path = draft.path().to_string_lossy().to_string();
raw = fs::read_to_string(&path)?;
}
}
}
if path.is_empty() {
return Err(anyhow!("Could not get path of message on disk"));
}
Ok((path, raw))
}
}