refactor: use Rust 1.77.0 support for recursion in async functions

This commit is contained in:
link2xt
2024-03-22 02:27:28 +00:00
parent 5bda4f0c26
commit 2f0f247e70
5 changed files with 119 additions and 141 deletions

View File

@@ -24,7 +24,7 @@ jobs:
name: Lint Rust name: Lint Rust
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
RUSTUP_TOOLCHAIN: 1.77.0 RUSTUP_TOOLCHAIN: 1.77.1
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@@ -83,15 +83,15 @@ jobs:
matrix: matrix:
include: include:
- os: ubuntu-latest - os: ubuntu-latest
rust: 1.77.0 rust: 1.77.1
- os: windows-latest - os: windows-latest
rust: 1.77.0 rust: 1.77.1
- os: macos-latest - os: macos-latest
rust: 1.77.0 rust: 1.77.1
# Minimum Supported Rust Version = 1.70.0 # Minimum Supported Rust Version = 1.77.0
- os: ubuntu-latest - os: ubuntu-latest
rust: 1.70.0 rust: 1.77.0
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View File

@@ -3,7 +3,7 @@ name = "deltachat"
version = "1.137.1" version = "1.137.1"
edition = "2021" edition = "2021"
license = "MPL-2.0" license = "MPL-2.0"
rust-version = "1.70" rust-version = "1.77"
repository = "https://github.com/deltachat/deltachat-core-rust" repository = "https://github.com/deltachat/deltachat-core-rust"
[profile.dev] [profile.dev]

View File

@@ -7,7 +7,7 @@ set -euo pipefail
# #
# Avoid using rustup here as it depends on reading /proc/self/exe and # Avoid using rustup here as it depends on reading /proc/self/exe and
# has problems running under QEMU. # has problems running under QEMU.
RUST_VERSION=1.77.0 RUST_VERSION=1.77.1
ARCH="$(uname -m)" ARCH="$(uname -m)"
test -f "/lib/libc.musl-$ARCH.so.1" && LIBC=musl || LIBC=gnu test -f "/lib/libc.musl-$ARCH.so.1" && LIBC=musl || LIBC=gnu

View File

@@ -7,12 +7,8 @@
//! `MsgId.get_html()` will return HTML - //! `MsgId.get_html()` will return HTML -
//! this allows nice quoting, handling linebreaks properly etc. //! this allows nice quoting, handling linebreaks properly etc.
use std::future::Future;
use std::pin::Pin;
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use base64::Engine as _; use base64::Engine as _;
use futures::future::FutureExt;
use lettre_email::mime::Mime; use lettre_email::mime::Mime;
use lettre_email::PartBuilder; use lettre_email::PartBuilder;
use mailparse::ParsedContentType; use mailparse::ParsedContentType;
@@ -116,119 +112,109 @@ impl HtmlMsgParser {
/// Usually, there is at most one plain-text and one HTML-text part, /// Usually, there is at most one plain-text and one HTML-text part,
/// multiple plain-text parts might be used for mailinglist-footers, /// multiple plain-text parts might be used for mailinglist-footers,
/// therefore we use the first one. /// therefore we use the first one.
fn collect_texts_recursive<'a>( async fn collect_texts_recursive<'a>(
&'a mut self, &'a mut self,
mail: &'a mailparse::ParsedMail<'a>, mail: &'a mailparse::ParsedMail<'a>,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a + Send>> { ) -> Result<()> {
// Boxed future to deal with recursion match get_mime_multipart_type(&mail.ctype) {
async move { MimeMultipartType::Multiple => {
match get_mime_multipart_type(&mail.ctype) { for cur_data in &mail.subparts {
MimeMultipartType::Multiple => { Box::pin(self.collect_texts_recursive(cur_data)).await?
for cur_data in &mail.subparts {
self.collect_texts_recursive(cur_data).await?
}
Ok(())
} }
MimeMultipartType::Message => { Ok(())
let raw = mail.get_body_raw()?; }
if raw.is_empty() { MimeMultipartType::Message => {
return Ok(()); let raw = mail.get_body_raw()?;
} if raw.is_empty() {
let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?; return Ok(());
self.collect_texts_recursive(&mail).await
} }
MimeMultipartType::Single => { let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?;
let mimetype = mail.ctype.mimetype.parse::<Mime>()?; Box::pin(self.collect_texts_recursive(&mail)).await
if mimetype == mime::TEXT_HTML { }
if self.html.is_empty() { MimeMultipartType::Single => {
if let Ok(decoded_data) = mail.get_body() { let mimetype = mail.ctype.mimetype.parse::<Mime>()?;
self.html = decoded_data; if mimetype == mime::TEXT_HTML {
} if self.html.is_empty() {
}
} else if mimetype == mime::TEXT_PLAIN && self.plain.is_none() {
if let Ok(decoded_data) = mail.get_body() { if let Ok(decoded_data) = mail.get_body() {
self.plain = Some(PlainText { self.html = decoded_data;
text: decoded_data,
flowed: if let Some(format) = mail.ctype.params.get("format") {
format.as_str().to_ascii_lowercase() == "flowed"
} else {
false
},
delsp: if let Some(delsp) = mail.ctype.params.get("delsp") {
delsp.as_str().to_ascii_lowercase() == "yes"
} else {
false
},
});
} }
} }
Ok(()) } else if mimetype == mime::TEXT_PLAIN && self.plain.is_none() {
if let Ok(decoded_data) = mail.get_body() {
self.plain = Some(PlainText {
text: decoded_data,
flowed: if let Some(format) = mail.ctype.params.get("format") {
format.as_str().to_ascii_lowercase() == "flowed"
} else {
false
},
delsp: if let Some(delsp) = mail.ctype.params.get("delsp") {
delsp.as_str().to_ascii_lowercase() == "yes"
} else {
false
},
});
}
} }
Ok(())
} }
} }
.boxed()
} }
/// Replace cid:-protocol by the data:-protocol where appropriate. /// Replace cid:-protocol by the data:-protocol where appropriate.
/// This allows the final html-file to be self-contained. /// This allows the final html-file to be self-contained.
fn cid_to_data_recursive<'a>( async fn cid_to_data_recursive<'a>(
&'a mut self, &'a mut self,
context: &'a Context, context: &'a Context,
mail: &'a mailparse::ParsedMail<'a>, mail: &'a mailparse::ParsedMail<'a>,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a + Send>> { ) -> Result<()> {
// Boxed future to deal with recursion match get_mime_multipart_type(&mail.ctype) {
async move { MimeMultipartType::Multiple => {
match get_mime_multipart_type(&mail.ctype) { for cur_data in &mail.subparts {
MimeMultipartType::Multiple => { Box::pin(self.cid_to_data_recursive(context, cur_data)).await?;
for cur_data in &mail.subparts {
self.cid_to_data_recursive(context, cur_data).await?;
}
Ok(())
} }
MimeMultipartType::Message => { Ok(())
let raw = mail.get_body_raw()?; }
if raw.is_empty() { MimeMultipartType::Message => {
return Ok(()); let raw = mail.get_body_raw()?;
} if raw.is_empty() {
let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?; return Ok(());
self.cid_to_data_recursive(context, &mail).await
} }
MimeMultipartType::Single => { let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?;
let mimetype = mail.ctype.mimetype.parse::<Mime>()?; Box::pin(self.cid_to_data_recursive(context, &mail)).await
if mimetype.type_() == mime::IMAGE { }
if let Some(cid) = mail.headers.get_header_value(HeaderDef::ContentId) { MimeMultipartType::Single => {
if let Ok(cid) = parse_message_id(&cid) { let mimetype = mail.ctype.mimetype.parse::<Mime>()?;
if let Ok(replacement) = mimepart_to_data_url(mail) { if mimetype.type_() == mime::IMAGE {
let re_string = format!( if let Some(cid) = mail.headers.get_header_value(HeaderDef::ContentId) {
"(<img[^>]*src[^>]*=[^>]*)(cid:{})([^>]*>)", if let Ok(cid) = parse_message_id(&cid) {
regex::escape(&cid) if let Ok(replacement) = mimepart_to_data_url(mail) {
); let re_string = format!(
match regex::Regex::new(&re_string) { "(<img[^>]*src[^>]*=[^>]*)(cid:{})([^>]*>)",
Ok(re) => { regex::escape(&cid)
self.html = re );
.replace_all( match regex::Regex::new(&re_string) {
&self.html, Ok(re) => {
format!("${{1}}{replacement}${{3}}").as_str(), self.html = re
) .replace_all(
.as_ref() &self.html,
.to_string() format!("${{1}}{replacement}${{3}}").as_str(),
} )
Err(e) => warn!( .as_ref()
context, .to_string()
"Cannot create regex for cid: {} throws {}",
re_string,
e
),
} }
Err(e) => warn!(
context,
"Cannot create regex for cid: {} throws {}", re_string, e
),
} }
} }
} }
} }
Ok(())
} }
Ok(())
} }
} }
.boxed()
} }
} }

View File

@@ -2,9 +2,7 @@
use std::cmp::min; use std::cmp::min;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::future::Future;
use std::path::Path; use std::path::Path;
use std::pin::Pin;
use std::str; use std::str;
use anyhow::{bail, Context as _, Result}; use anyhow::{bail, Context as _, Result};
@@ -818,59 +816,53 @@ impl MimeMessage {
self.headers.get(headerdef.get_headername()) self.headers.get(headerdef.get_headername())
} }
fn parse_mime_recursive<'a>( async fn parse_mime_recursive<'a>(
&'a mut self, &'a mut self,
context: &'a Context, context: &'a Context,
mail: &'a mailparse::ParsedMail<'a>, mail: &'a mailparse::ParsedMail<'a>,
is_related: bool, is_related: bool,
) -> Pin<Box<dyn Future<Output = Result<bool>> + 'a + Send>> { ) -> Result<bool> {
use futures::future::FutureExt; enum MimeS {
Multiple,
Single,
Message,
}
// Boxed future to deal with recursion let mimetype = mail.ctype.mimetype.to_lowercase();
async move {
enum MimeS {
Multiple,
Single,
Message,
}
let mimetype = mail.ctype.mimetype.to_lowercase(); let m = if mimetype.starts_with("multipart") {
if mail.ctype.params.contains_key("boundary") {
let m = if mimetype.starts_with("multipart") { MimeS::Multiple
if mail.ctype.params.contains_key("boundary") {
MimeS::Multiple
} else {
MimeS::Single
}
} else if mimetype.starts_with("message") {
if mimetype == "message/rfc822" && !is_attachment_disposition(mail) {
MimeS::Message
} else {
MimeS::Single
}
} else { } else {
MimeS::Single MimeS::Single
}; }
} else if mimetype.starts_with("message") {
if mimetype == "message/rfc822" && !is_attachment_disposition(mail) {
MimeS::Message
} else {
MimeS::Single
}
} else {
MimeS::Single
};
let is_related = is_related || mimetype == "multipart/related"; let is_related = is_related || mimetype == "multipart/related";
match m { match m {
MimeS::Multiple => self.handle_multiple(context, mail, is_related).await, MimeS::Multiple => Box::pin(self.handle_multiple(context, mail, is_related)).await,
MimeS::Message => { MimeS::Message => {
let raw = mail.get_body_raw()?; let raw = mail.get_body_raw()?;
if raw.is_empty() { if raw.is_empty() {
return Ok(false); return Ok(false);
} }
let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?; let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?;
self.parse_mime_recursive(context, &mail, is_related).await Box::pin(self.parse_mime_recursive(context, &mail, is_related)).await
} }
MimeS::Single => { MimeS::Single => {
self.add_single_part_if_known(context, mail, is_related) self.add_single_part_if_known(context, mail, is_related)
.await .await
}
} }
} }
.boxed()
} }
async fn handle_multiple( async fn handle_multiple(