Merge pull request #657 from matthiasbeyer/libimagmail/init

Libimagmail/init
This commit is contained in:
Matthias Beyer 2016-10-14 11:00:44 +02:00 committed by GitHub
commit 1d3666eef4
8 changed files with 283 additions and 1 deletions

21
libimagmail/Cargo.toml Normal file
View file

@ -0,0 +1,21 @@
[package]
name = "libimagmail"
version = "0.2.0"
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
[dependencies]
log = "0.3"
mailparse = "0.2.0"
semver = "0.2"
toml = "0.2.*"
filters = "0.1.0"
[dependencies.libimagstore]
path = "../libimagstore"
[dependencies.libimagerror]
path = "../libimagerror"
[dependencies.libimagref]
path = "../libimagref"

16
libimagmail/src/error.rs Normal file
View file

@ -0,0 +1,16 @@
generate_error_module!(
generate_error_types!(MailError, MailErrorKind,
RefCreationError => "Error creating a reference to a file/directory",
RefHandlingError => "Error while handling the internal reference object",
MailParsingError => "Error while parsing mail",
FetchByHashError => "Error fetching mail from Store by hash",
FetchError => "Error fetching mail from Store",
IOError => "IO Error"
);
);
pub use self::error::MailError;
pub use self::error::MailErrorKind;
pub use self::error::MapErrInto;

64
libimagmail/src/hasher.rs Normal file
View file

@ -0,0 +1,64 @@
use std::io::Read;
use std::path::PathBuf;
use mailparse::{MailHeader, parse_mail};
use libimagref::hasher::Hasher;
use libimagref::hasher::DefaultHasher;
use libimagref::error::RefErrorKind as REK;
use libimagref::error::MapErrInto;
use libimagref::result::Result as RResult;
use libimagerror::into::IntoError;
use error::MailErrorKind as MEK;
pub struct MailHasher {
defaulthasher: DefaultHasher,
}
impl MailHasher {
pub fn new() -> MailHasher {
MailHasher { defaulthasher: DefaultHasher::new() }
}
}
impl Hasher for MailHasher {
fn hash_name(&self) -> &'static str {
"default_mail_hasher"
}
fn create_hash<R: Read>(&mut self, pb: &PathBuf, c: &mut R) -> RResult<String> {
use filters::filter::Filter;
let mut s = String::new();
try!(c.read_to_string(&mut s).map_err_into(REK::UTF8Error).map_err_into(REK::IOError));
parse_mail(&s.as_bytes())
.map_err(Box::new)
.map_err(|e| MEK::MailParsingError.into_error_with_cause(e))
.map_err_into(REK::RefHashingError)
.and_then(|mail| {
let has_key = |hdr: &MailHeader, exp: &str|
hdr.get_key().map(|s| s == exp).unwrap_or(false);
let subject_filter = |hdr: &MailHeader| has_key(hdr, "Subject");
let from_filter = |hdr: &MailHeader| has_key(hdr, "From");
let to_filter = |hdr: &MailHeader| has_key(hdr, "To");
let filter = subject_filter.or(from_filter).or(to_filter);
let s : String = mail.headers
.iter()
.filter(|item| filter.filter(item))
.filter_map(|hdr| hdr.get_value().ok()) // TODO: Do not hide error here
.collect::<Vec<String>>()
.join("");
self.defaulthasher.create_hash(pb, &mut s.as_bytes())
})
}
}

37
libimagmail/src/iter.rs Normal file
View file

@ -0,0 +1,37 @@
//! Module for the MailIter
//!
//! MailIter is a iterator which takes an Iterator that yields `Ref` and yields itself
//! `Result<Mail>`, where `Err(_)` is returned if the Ref is not a Mail or parsing of the
//! referenced mail file failed.
//!
use mail::Mail;
use result::Result;
use libimagref::reference::Ref;
use std::marker::PhantomData;
struct MailIter<'a, I: 'a + Iterator<Item = Ref<'a>>> {
_marker: PhantomData<&'a I>,
i: I,
}
impl<'a, I: Iterator<Item = Ref<'a>>> MailIter<'a, I> {
pub fn new(i: I) -> MailIter<'a, I> {
MailIter { _marker: PhantomData, i: i }
}
}
impl<'a, I: Iterator<Item = Ref<'a>>> Iterator for MailIter<'a, I> {
type Item = Result<Mail<'a>>;
fn next(&mut self) -> Option<Result<Mail<'a>>> {
self.i.next().map(Mail::from_ref)
}
}

16
libimagmail/src/lib.rs Normal file
View file

@ -0,0 +1,16 @@
#[macro_use] extern crate log;
extern crate mailparse;
extern crate semver;
extern crate toml;
extern crate filters;
#[macro_use] extern crate libimagerror;
extern crate libimagstore;
extern crate libimagref;
pub mod error;
pub mod hasher;
pub mod iter;
pub mod mail;
pub mod result;

120
libimagmail/src/mail.rs Normal file
View file

@ -0,0 +1,120 @@
use std::result::Result as RResult;
use std::path::Path;
use std::path::PathBuf;
use std::fs::File;
use std::io::Read;
use libimagstore::store::{FileLockEntry, Store};
use libimagref::reference::Ref;
use libimagref::flags::RefFlags;
use mailparse::{MailParseError, ParsedMail, parse_mail};
use hasher::MailHasher;
use result::Result;
use error::{MapErrInto, MailErrorKind as MEK};
struct Buffer(String);
impl Buffer {
pub fn parsed<'a>(&'a self) -> RResult<ParsedMail<'a>, MailParseError> {
parse_mail(self.0.as_bytes())
}
}
impl From<String> for Buffer {
fn from(data: String) -> Buffer {
Buffer(data)
}
}
pub struct Mail<'a>(Ref<'a>, Buffer);
impl<'a> Mail<'a> {
/// Imports a mail from the Path passed
pub fn import_from_path<P: AsRef<Path>>(store: &Store, p: P) -> Result<Mail> {
let h = MailHasher::new();
let f = RefFlags::default().with_content_hashing(true).with_permission_tracking(false);
let p = PathBuf::from(p.as_ref());
Ref::create_with_hasher(store, p, f, h)
.map_err_into(MEK::RefCreationError)
.and_then(|reference| {
reference.fs_file()
.map_err_into(MEK::RefHandlingError)
.and_then(|path| File::open(path).map_err_into(MEK::IOError))
.and_then(|mut file| {
let mut s = String::new();
file.read_to_string(&mut s)
.map(|_| s)
.map_err_into(MEK::IOError)
})
.map(Buffer::from)
.map(|buffer| Mail(reference, buffer))
})
}
/// Opens a mail by the passed hash
pub fn open<S: AsRef<str>>(store: &Store, hash: S) -> Result<Option<Mail>> {
Ref::get_by_hash(store, String::from(hash.as_ref()))
.map_err_into(MEK::FetchByHashError)
.map_err_into(MEK::FetchError)
.and_then(|o| match o {
Some(r) => Mail::from_ref(r).map(Some),
None => Ok(None),
})
}
/// Implement me as TryFrom as soon as it is stable
pub fn from_ref(r: Ref<'a>) -> Result<Mail> {
r.fs_file()
.map_err_into(MEK::RefHandlingError)
.and_then(|path| File::open(path).map_err_into(MEK::IOError))
.and_then(|mut file| {
let mut s = String::new();
file.read_to_string(&mut s)
.map(|_| s)
.map_err_into(MEK::IOError)
})
.map(Buffer::from)
.map(|buffer| Mail(r, buffer))
}
pub fn get_field(&self, field: &str) -> Result<Option<String>> {
use mailparse::MailHeader;
self.1
.parsed()
.map_err_into(MEK::MailParsingError)
.map(|parsed| {
parsed.headers
.iter()
.filter(|hdr| hdr.get_key().map(|n| n == field).unwrap_or(false))
.next()
.and_then(|field| field.get_value().ok())
})
}
pub fn get_from(&self) -> Result<Option<String>> {
self.get_field("From")
}
pub fn get_to(&self) -> Result<Option<String>> {
self.get_field("To")
}
pub fn get_subject(&self) -> Result<Option<String>> {
self.get_field("Subject")
}
pub fn get_message_id(&self) -> Result<Option<String>> {
self.get_field("Message-ID")
}
pub fn get_in_reply_to(&self) -> Result<Option<String>> {
self.get_field("In-Reply-To")
}
}

View file

@ -0,0 +1,6 @@
use std::result::Result as RResult;
use error::MailError;
pub type Result<T> = RResult<T, MailError>;

View file

@ -41,7 +41,9 @@ generate_error_module!(
RefTargetPermissionError => "Ref Target permissions insufficient for referencing",
RefTargetCannotBeHashed => "Ref Target cannot be hashed (is it a directory?)",
RefTargetFileCannotBeOpened => "Ref Target File cannot be open()ed",
RefTargetCannotReadPermissions => "Ref Target: Cannot read permissions"
RefTargetCannotReadPermissions => "Ref Target: Cannot read permissions",
RefHashingError => "Error while hashing"
);
);