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 quirks::{Odyssey, nop};
use regex::Regex;
use crate::model::Draftbox;
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, &msg.sender_addr)?;
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", "-g", "!signatures", from, &path).read()?);
Ok((rc, account))
}
fn maildirs(account: &String, from: &String) -> Result<Draftbox> {
if Draftbox::exists() {
let draftboxes = Draftbox::load()?;
match draftboxes.iter().find(|d| d.address == *from) {
Some(draftbox) => return Ok(draftbox.to_owned()),
None => nop!(),
}
}
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 = map
.get("folder")
.expect("You must specify the top-level maildir in your account file")
.expand()?;
let postponed = map
.get("postponed")
.expect("You must specify the postponed maildir in your account file")
.replace(|c: char| !c.is_alphanumeric(), "");
Ok(Draftbox::new(from, &format!("{}/{}", folder, postponed)))
}
fn draft(draftbox: Draftbox, id: &String) -> Result<(String, String)> {
let mut path = String::new();
let mut raw = String::new();
for maildir in draftbox.folders() {
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))
}
}