mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-17 13:17:55 +08:00
init notmuch backend
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use crate::{
|
||||
backends::Backend,
|
||||
config::{AccountConfig, NotmuchBackendConfig},
|
||||
mbox::Mboxes,
|
||||
msg::{Envelopes, Msg},
|
||||
};
|
||||
|
||||
use super::NotmuchEnvelopes;
|
||||
|
||||
pub struct NotmuchBackend<'a> {
|
||||
account_config: &'a AccountConfig,
|
||||
db: notmuch::Database,
|
||||
}
|
||||
|
||||
impl<'a> NotmuchBackend<'a> {
|
||||
pub fn new(
|
||||
account_config: &'a AccountConfig,
|
||||
notmuch_config: &'a NotmuchBackendConfig,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
account_config,
|
||||
db: notmuch::Database::open(
|
||||
notmuch_config.notmuch_database_dir.clone(),
|
||||
notmuch::DatabaseMode::ReadWrite,
|
||||
)
|
||||
.context(format!(
|
||||
"cannot open notmuch database at {:?}",
|
||||
notmuch_config.notmuch_database_dir
|
||||
))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Backend<'a> for NotmuchBackend<'a> {
|
||||
fn add_mbox(&mut self, mdir: &str) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn get_mboxes(&mut self) -> Result<Box<dyn Mboxes>> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn del_mbox(&mut self, mdir: &str) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn get_envelopes(
|
||||
&mut self,
|
||||
mdir: &str,
|
||||
_sort: &str,
|
||||
filter: &str,
|
||||
page_size: usize,
|
||||
page: usize,
|
||||
) -> Result<Box<dyn Envelopes>> {
|
||||
let query = self
|
||||
.db
|
||||
.create_query(filter)
|
||||
.context("cannot create query")?;
|
||||
let msgs: NotmuchEnvelopes = query
|
||||
.search_messages()
|
||||
.context("cannot get messages")?
|
||||
.try_into()?;
|
||||
Ok(Box::new(msgs))
|
||||
}
|
||||
|
||||
fn add_msg(&mut self, mdir: &str, msg: &[u8], flags: &str) -> Result<Box<dyn ToString>> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn get_msg(&mut self, mdir: &str, id: &str) -> Result<Msg> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn copy_msg(&mut self, mdir_src: &str, mdir_dst: &str, id: &str) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn move_msg(&mut self, mdir_src: &str, mdir_dst: &str, id: &str) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn del_msg(&mut self, mdir: &str, id: &str) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn add_flags(&mut self, mdir: &str, id: &str, flags_str: &str) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn set_flags(&mut self, mdir: &str, id: &str, flags_str: &str) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn del_flags(&mut self, mdir: &str, id: &str, flags_str: &str) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
//! Notmuch mailbox module.
|
||||
//!
|
||||
//! This module provides Notmuch types and conversion utilities
|
||||
//! related to the envelope
|
||||
|
||||
use anyhow::{anyhow, Context, Error, Result};
|
||||
use chrono::DateTime;
|
||||
use log::{info, trace};
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
msg::{from_slice_to_addrs, Addr},
|
||||
output::{PrintTable, PrintTableOpts, WriteColor},
|
||||
ui::{Cell, Row, Table},
|
||||
};
|
||||
|
||||
/// Represents a list of envelopes.
|
||||
#[derive(Debug, Default, serde::Serialize)]
|
||||
pub struct NotmuchEnvelopes(pub Vec<NotmuchEnvelope>);
|
||||
|
||||
impl Deref for NotmuchEnvelopes {
|
||||
type Target = Vec<NotmuchEnvelope>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for NotmuchEnvelopes {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for NotmuchEnvelopes {
|
||||
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||
writeln!(writter)?;
|
||||
Table::print(writter, self, opts)?;
|
||||
writeln!(writter)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the envelope. The envelope is just a message subset,
|
||||
/// and is mostly used for listings.
|
||||
#[derive(Debug, Default, Clone, serde::Serialize)]
|
||||
pub struct NotmuchEnvelope {
|
||||
/// Represents the id of the message.
|
||||
pub id: String,
|
||||
|
||||
/// Represents the tags of the message.
|
||||
pub flags: Vec<String>,
|
||||
|
||||
/// Represents the subject of the message.
|
||||
pub subject: String,
|
||||
|
||||
/// Represents the first sender of the message.
|
||||
pub sender: String,
|
||||
|
||||
/// Represents the date of the message.
|
||||
pub date: String,
|
||||
}
|
||||
|
||||
impl Table for NotmuchEnvelope {
|
||||
fn head() -> Row {
|
||||
Row::new()
|
||||
.cell(Cell::new("ID").bold().underline().white())
|
||||
.cell(Cell::new("FLAGS").bold().underline().white())
|
||||
.cell(Cell::new("SUBJECT").shrinkable().bold().underline().white())
|
||||
.cell(Cell::new("SENDER").bold().underline().white())
|
||||
.cell(Cell::new("DATE").bold().underline().white())
|
||||
}
|
||||
|
||||
fn row(&self) -> Row {
|
||||
let id = self.id.to_string();
|
||||
let unseen = !self.flags.contains(&String::from("unread"));
|
||||
let flags = String::new();
|
||||
let subject = &self.subject;
|
||||
let sender = &self.sender;
|
||||
let date = &self.date;
|
||||
Row::new()
|
||||
.cell(Cell::new(id).bold_if(unseen).red())
|
||||
.cell(Cell::new(flags).bold_if(unseen).white())
|
||||
.cell(Cell::new(subject).shrinkable().bold_if(unseen).green())
|
||||
.cell(Cell::new(sender).bold_if(unseen).blue())
|
||||
.cell(Cell::new(date).bold_if(unseen).yellow())
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a list of raw envelopees returned by the `notmuch` crate.
|
||||
pub type RawNotmuchEnvelopes = notmuch::Messages;
|
||||
|
||||
impl<'a> TryFrom<RawNotmuchEnvelopes> for NotmuchEnvelopes {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(raw_envelopes: RawNotmuchEnvelopes) -> Result<Self, Self::Error> {
|
||||
let mut envelopes = vec![];
|
||||
for raw_envelope in raw_envelopes {
|
||||
let envelope: NotmuchEnvelope = raw_envelope
|
||||
.try_into()
|
||||
.context("cannot parse notmuch mail entry")?;
|
||||
envelopes.push(envelope);
|
||||
}
|
||||
Ok(NotmuchEnvelopes(envelopes))
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the raw envelope returned by the `notmuch` crate.
|
||||
pub type RawNotmuchEnvelope = notmuch::Message;
|
||||
|
||||
impl<'a> TryFrom<RawNotmuchEnvelope> for NotmuchEnvelope {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(raw_envelope: RawNotmuchEnvelope) -> Result<Self, Self::Error> {
|
||||
info!("begin: try building envelope from notmuch parsed mail");
|
||||
|
||||
let id = raw_envelope.id().trim().to_string();
|
||||
let subject = raw_envelope
|
||||
.header("subject")
|
||||
.context("cannot get header \"Subject\" from notmuch message")?
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
let sender = raw_envelope
|
||||
.header("from")
|
||||
.context("cannot get header \"From\" from notmuch message")?
|
||||
.ok_or_else(|| anyhow!("cannot parse sender from notmuch message {:?}", id))?
|
||||
.to_string();
|
||||
let sender = from_slice_to_addrs(sender)?
|
||||
.and_then(|senders| {
|
||||
if senders.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(senders)
|
||||
}
|
||||
})
|
||||
.map(|senders| match &senders[0] {
|
||||
Addr::Single(mailparse::SingleInfo { display_name, addr }) => {
|
||||
display_name.as_ref().unwrap_or_else(|| addr).to_owned()
|
||||
}
|
||||
Addr::Group(mailparse::GroupInfo { group_name, .. }) => group_name.to_owned(),
|
||||
})
|
||||
.ok_or_else(|| anyhow!("cannot find sender"))?;
|
||||
let date = raw_envelope
|
||||
.header("date")
|
||||
.context("cannot get header \"Date\" from notmuch message")?
|
||||
.ok_or_else(|| anyhow!("cannot parse date of notmuch message {:?}", id))?
|
||||
.to_string();
|
||||
let date =
|
||||
DateTime::parse_from_rfc2822(date.split_at(date.find(" (").unwrap_or(date.len())).0)
|
||||
.context(format!(
|
||||
"cannot parse message date {:?} of notmuch message {:?}",
|
||||
date, id
|
||||
))?
|
||||
.naive_local()
|
||||
.to_string();
|
||||
|
||||
let envelope = Self {
|
||||
id,
|
||||
flags: raw_envelope.tags().collect(),
|
||||
subject,
|
||||
sender,
|
||||
date,
|
||||
};
|
||||
trace!("envelope: {:?}", envelope);
|
||||
|
||||
info!("end: try building envelope from notmuch parsed mail");
|
||||
Ok(envelope)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user