Aerogramme refactoring #57

Merged
quentin merged 16 commits from feat/more-imap-qol into main 2024-01-06 10:38:37 +00:00
Showing only changes of commit 0e7595d65a - Show all commits

View file

@ -5,15 +5,18 @@ use std::collections::HashSet;
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Result};
use imap_codec::imap_types::body::{BasicFields, Body as FetchBody, BodyStructure, SpecificFields}; use imap_codec::imap_types::body::{BasicFields, Body as FetchBody, BodyStructure, SpecificFields};
use imap_codec::imap_types::core::{AString, IString, NonEmptyVec}; use imap_codec::imap_types::core::{AString, IString, NString, NonEmptyVec};
use imap_codec::imap_types::fetch::{ use imap_codec::imap_types::fetch::{
Section as FetchSection, Part as FetchPart Section as FetchSection, Part as FetchPart
}; };
use eml_codec::{ use eml_codec::{
header, part::AnyPart, part::composite, part::discrete, header, mime,
part::AnyPart, part::composite, part::discrete,
}; };
use crate::imap::imf_view::message_envelope;
pub enum BodySection<'a> { pub enum BodySection<'a> {
Full(Cow<'a, [u8]>), Full(Cow<'a, [u8]>),
@ -80,14 +83,13 @@ pub fn body_ext<'a>(
/// b OK Fetch completed (0.001 + 0.000 secs). /// b OK Fetch completed (0.001 + 0.000 secs).
/// ``` /// ```
pub fn bodystructure(part: &AnyPart) -> Result<BodyStructure<'static>> { pub fn bodystructure(part: &AnyPart) -> Result<BodyStructure<'static>> {
unimplemented!(); NodeMime(part).structure()
} }
/// NodeMime /// NodeMime
/// ///
/// Used for recursive logic on MIME.
/// See SelectedMime for inspection.
struct NodeMime<'a>(&'a AnyPart<'a>); struct NodeMime<'a>(&'a AnyPart<'a>);
impl<'a> NodeMime<'a> { impl<'a> NodeMime<'a> {
/// A MIME object is a tree of elements. /// A MIME object is a tree of elements.
@ -119,6 +121,15 @@ impl<'a> NodeMime<'a> {
} }
} }
} }
fn structure(&self) -> Result<BodyStructure<'static>> {
match self.0 {
AnyPart::Txt(x) => NodeTxt(self, x).structure(),
AnyPart::Bin(x) => NodeBin(self, x).structure(),
AnyPart::Mult(x) => NodeMult(self, x).structure(),
AnyPart::Msg(x) => NodeMsg(self, x).structure(),
}
}
} }
//---------------------------------------------------------- //----------------------------------------------------------
@ -150,6 +161,9 @@ impl<'a> SubsettedSection<'a> {
} }
} }
/// Used for current MIME inspection
///
/// See NodeMime for recursive logic
struct SelectedMime<'a>(&'a AnyPart<'a>); struct SelectedMime<'a>(&'a AnyPart<'a>);
impl<'a> SelectedMime<'a> { impl<'a> SelectedMime<'a> {
/// The subsetted fetch section basically tells us the /// The subsetted fetch section basically tells us the
@ -254,17 +268,109 @@ impl<'a> SelectedMime<'a> {
// ------------ // ------------
/// Returns the structure of the message /// Basic field of a MIME part that is
fn structure(&self) -> Result<BodyStructure<'static>> { /// common to all parts
unimplemented!(); fn basic_fields(&self) -> Result<BasicFields<'static>> {
let sz = match self.0 {
AnyPart::Txt(x) => x.body.len(),
AnyPart::Bin(x) => x.body.len(),
AnyPart::Msg(x) => x.raw_part.len(),
AnyPart::Mult(x) => 0
};
let m = self.0.mime();
let parameter_list = m
.ctype
.as_ref()
.map(|x| {
x.params
.iter()
.map(|p| {
(
IString::try_from(String::from_utf8_lossy(p.name).to_string()),
IString::try_from(p.value.to_string()),
)
})
.filter(|(k, v)| k.is_ok() && v.is_ok())
.map(|(k, v)| (k.unwrap(), v.unwrap()))
.collect()
})
.unwrap_or(vec![]);
Ok(BasicFields {
parameter_list,
id: NString(
m.id.as_ref()
.and_then(|ci| IString::try_from(ci.to_string()).ok()),
),
description: NString(
m.description
.as_ref()
.and_then(|cd| IString::try_from(cd.to_string()).ok()),
),
content_transfer_encoding: match m.transfer_encoding {
mime::mechanism::Mechanism::_8Bit => unchecked_istring("8bit"),
mime::mechanism::Mechanism::Binary => unchecked_istring("binary"),
mime::mechanism::Mechanism::QuotedPrintable => unchecked_istring("quoted-printable"),
mime::mechanism::Mechanism::Base64 => unchecked_istring("base64"),
_ => unchecked_istring("7bit"),
},
// @FIXME we can't compute the size of the message currently...
size: u32::try_from(sz)?,
})
} }
} }
// --------------------------- // ---------------------------
struct SelectedMsg<'a>(&'a SelectedMime<'a>, &'a composite::Message<'a>); struct NodeMsg<'a>(&'a NodeMime<'a>, &'a composite::Message<'a>);
struct SelectedMult<'a>(&'a SelectedMime<'a>, &'a composite::Multipart<'a>); impl<'a> NodeMsg<'a> {
struct SelectedTxt<'a>(&'a SelectedMime<'a>, &'a discrete::Text<'a>); fn structure(&self) -> Result<BodyStructure<'static>> {
struct SelectedBin<'a>(&'a SelectedMime<'a>, &'a discrete::Binary<'a>); let basic = SelectedMime(self.0.0).basic_fields()?;
Ok(BodyStructure::Single {
body: FetchBody {
basic,
specific: SpecificFields::Message {
envelope: Box::new(message_envelope(&self.1.imf)),
body_structure: Box::new(NodeMime(&self.1.child).structure()?),
number_of_lines: nol(self.1.raw_part),
},
},
extension_data: None,
})
}
}
struct NodeMult<'a>(&'a NodeMime<'a>, &'a composite::Multipart<'a>);
impl<'a> NodeMult<'a> {
fn structure(&self) -> Result<BodyStructure<'static>> {
let itype = &self.1.mime.interpreted_type;
let subtype = IString::try_from(itype.subtype.to_string())
.unwrap_or(unchecked_istring("alternative"));
let inner_bodies = self.1
.children
.iter()
.filter_map(|inner| NodeMime(&inner).structure().ok())
.collect::<Vec<_>>();
NonEmptyVec::validate(&inner_bodies)?;
let bodies = NonEmptyVec::unvalidated(inner_bodies);
Ok(BodyStructure::Multi {
bodies,
subtype,
extension_data: None,
/*Some(MultipartExtensionData {
parameter_list: vec![],
disposition: None,
language: None,
location: None,
extension: vec![],
})*/
})
}
}
struct NodeTxt<'a>(&'a NodeMime<'a>, &'a discrete::Text<'a>);
struct NodeBin<'a>(&'a NodeMime<'a>, &'a discrete::Binary<'a>);
// --------------------------- // ---------------------------
@ -329,3 +435,21 @@ impl<'a> ExtractedFull<'a> {
} }
} }
} }
/// ---- LEGACY
/// s is set to static to ensure that only compile time values
/// checked by developpers are passed.
fn unchecked_istring(s: &'static str) -> IString {
IString::try_from(s).expect("this value is expected to be a valid imap-codec::IString")
}
// Number Of Lines
fn nol(input: &[u8]) -> u32 {
input
.iter()
.filter(|x| **x == b'\n')
.count()
.try_into()
.unwrap_or(0)
}