build: update all crates to Rust 2024 edition

Largest change is in the FFI crate.
With 2024 (but not 2021) edition unsafe code
inside unsafe functions should be marked separately
so we can mark exactly the code that is unsafe.
Some CFFI functions even have no unsafe code inside.

Most interesting change is that .strdup()
functions are not marked as unsafe anymore.
They are allocating memory and return raw pointers,
but there is nothing unsafe about it.
Only using the returned raw pointers is unsafe.
This way calls to .strdup() don't have to be marked
with unsafe{} blocks.
This commit is contained in:
link2xt
2026-07-22 12:50:53 +00:00
committed by l
parent 00e1d00dfa
commit 2d7af15124
24 changed files with 977 additions and 1049 deletions

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "deltachat-contact-tools" name = "deltachat-contact-tools"
version = "0.0.0" # No semver-stable versioning version = "0.0.0" # No semver-stable versioning
edition = "2021" edition = "2024"
description = "Contact-related tools, like parsing vcards and sanitizing name and address. Meant for internal use in the deltachat crate." description = "Contact-related tools, like parsing vcards and sanitizing name and address. Meant for internal use in the deltachat crate."
license = "MPL-2.0" license = "MPL-2.0"

View File

@@ -30,12 +30,12 @@
use std::fmt; use std::fmt;
use std::ops::Deref; use std::ops::Deref;
use anyhow::bail;
use anyhow::Result; use anyhow::Result;
use anyhow::bail;
use regex::regex; use regex::regex;
mod vcard; mod vcard;
pub use vcard::{make_vcard, parse_vcard, VcardContact}; pub use vcard::{VcardContact, make_vcard, parse_vcard};
/// Valid contact address. /// Valid contact address.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]

View File

@@ -220,7 +220,10 @@ END:VCARD
assert_eq!(contacts[0].addr, "bob@example.org".to_string()); assert_eq!(contacts[0].addr, "bob@example.org".to_string());
assert_eq!(contacts[0].authname, "Bob".to_string()); assert_eq!(contacts[0].authname, "Bob".to_string());
assert_eq!(contacts[0].key, None); assert_eq!(contacts[0].key, None);
assert_eq!(contacts[0].profile_image.as_deref().unwrap(), "/9j/4AAQSkZJRgABAQAAAQABAAD/4gIoSUNDX1BST0ZJTEUAAQEAAAIYAAAAAAQwAABtbnRyUkdCIFhZWiAAAAAAAAAAAAAAAABhY3NwAAAAAAAAAAAAAAAAL8bRuAJYoZUYrI4ZY3VWwxw4Ay28AAGBISScmf/2Q=="); assert_eq!(
contacts[0].profile_image.as_deref().unwrap(),
"/9j/4AAQSkZJRgABAQAAAQABAAD/4gIoSUNDX1BST0ZJTEUAAQEAAAIYAAAAAAQwAABtbnRyUkdCIFhZWiAAAAAAAAAAAAAAAABhY3NwAAAAAAAAAAAAAAAAL8bRuAJYoZUYrI4ZY3VWwxw4Ay28AAGBISScmf/2Q=="
);
} }
} }
@@ -244,7 +247,10 @@ END:VCARD",
assert_eq!(contacts.len(), 1); assert_eq!(contacts.len(), 1);
assert_eq!(&contacts[0].addr, "alice@example.org"); assert_eq!(&contacts[0].addr, "alice@example.org");
assert_eq!(&contacts[0].authname, "Alice Wonderland"); assert_eq!(&contacts[0].authname, "Alice Wonderland");
assert_eq!(contacts[0].key.as_ref().unwrap(), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); assert_eq!(
contacts[0].key.as_ref().unwrap(),
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
);
assert!(contacts[0].timestamp.is_err()); assert!(contacts[0].timestamp.is_err());
assert_eq!(contacts[0].profile_image, None); assert_eq!(contacts[0].profile_image, None);
} }
@@ -272,9 +278,15 @@ END:VCARD",
assert_eq!(contacts.len(), 1); assert_eq!(contacts.len(), 1);
assert_eq!(&contacts[0].addr, "alice@example.org"); assert_eq!(&contacts[0].addr, "alice@example.org");
assert_eq!(&contacts[0].authname, "Alice"); assert_eq!(&contacts[0].authname, "Alice");
assert_eq!(contacts[0].key.as_ref().unwrap(), "xsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=="); assert_eq!(
contacts[0].key.as_ref().unwrap(),
"xsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=="
);
assert!(contacts[0].timestamp.is_err()); assert!(contacts[0].timestamp.is_err());
assert_eq!(contacts[0].profile_image.as_ref().unwrap(), "/9aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Z"); assert_eq!(
contacts[0].profile_image.as_ref().unwrap(),
"/9aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Z"
);
} }
#[test] #[test]

View File

@@ -2,7 +2,7 @@
name = "deltachat_ffi" name = "deltachat_ffi"
version = "2.58.0-dev" version = "2.58.0-dev"
description = "Deltachat FFI" description = "Deltachat FFI"
edition = "2018" edition = "2024"
readme = "README.md" readme = "README.md"
license = "MPL-2.0" license = "MPL-2.0"

File diff suppressed because it is too large Load Diff

View File

@@ -17,13 +17,15 @@ use std::ptr;
/// } /// }
/// ``` /// ```
unsafe fn dc_strdup(s: *const libc::c_char) -> *mut libc::c_char { unsafe fn dc_strdup(s: *const libc::c_char) -> *mut libc::c_char {
let ret: *mut libc::c_char = if !s.is_null() { unsafe {
libc::strdup(s) let ret: *mut libc::c_char = if !s.is_null() {
} else { libc::strdup(s)
libc::calloc(1, 1) as *mut libc::c_char } else {
}; libc::calloc(1, 1) as *mut libc::c_char
assert!(!ret.is_null()); };
ret assert!(!ret.is_null());
ret
}
} }
/// Error type for the [OsStrExt] trait /// Error type for the [OsStrExt] trait
@@ -164,34 +166,40 @@ pub(crate) trait Strdup {
/// This function will panic when the original string contains an /// This function will panic when the original string contains an
/// interior null byte as this can not be represented in raw C /// interior null byte as this can not be represented in raw C
/// strings. /// strings.
unsafe fn strdup(&self) -> *mut libc::c_char; fn strdup(&self) -> *mut libc::c_char;
} }
impl Strdup for str { impl Strdup for str {
unsafe fn strdup(&self) -> *mut libc::c_char { fn strdup(&self) -> *mut libc::c_char {
let tmp = CString::new_lossy(self); unsafe {
dc_strdup(tmp.as_ptr()) let tmp = CString::new_lossy(self);
dc_strdup(tmp.as_ptr())
}
} }
} }
impl Strdup for String { impl Strdup for String {
unsafe fn strdup(&self) -> *mut libc::c_char { fn strdup(&self) -> *mut libc::c_char {
let s: &str = self; let s: &str = self;
s.strdup() s.strdup()
} }
} }
impl Strdup for std::path::Path { impl Strdup for std::path::Path {
unsafe fn strdup(&self) -> *mut libc::c_char { fn strdup(&self) -> *mut libc::c_char {
let tmp = self.to_c_string().unwrap_or_else(|_| CString::default()); unsafe {
dc_strdup(tmp.as_ptr()) let tmp = self.to_c_string().unwrap_or_else(|_| CString::default());
dc_strdup(tmp.as_ptr())
}
} }
} }
impl Strdup for [u8] { impl Strdup for [u8] {
unsafe fn strdup(&self) -> *mut libc::c_char { fn strdup(&self) -> *mut libc::c_char {
let tmp = CString::new_lossy(self); unsafe {
dc_strdup(tmp.as_ptr()) let tmp = CString::new_lossy(self);
dc_strdup(tmp.as_ptr())
}
} }
} }
@@ -209,15 +217,15 @@ pub(crate) trait OptStrdup {
/// Allocate a new raw C `*char` version of this string, or NULL. /// Allocate a new raw C `*char` version of this string, or NULL.
/// ///
/// See [Strdup::strdup] for details. /// See [Strdup::strdup] for details.
unsafe fn strdup(&self) -> *mut libc::c_char; fn strdup(&self) -> *mut libc::c_char;
} }
impl<T: AsRef<str>> OptStrdup for Option<T> { impl<T: AsRef<str>> OptStrdup for Option<T> {
unsafe fn strdup(&self) -> *mut libc::c_char { fn strdup(&self) -> *mut libc::c_char {
match self { match self {
Some(s) => { Some(s) => {
let tmp = CString::new_lossy(s.as_ref()); let tmp = CString::new_lossy(s.as_ref());
dc_strdup(tmp.as_ptr()) unsafe { dc_strdup(tmp.as_ptr()) }
} }
None => ptr::null_mut(), None => ptr::null_mut(),
} }
@@ -258,11 +266,9 @@ pub(crate) fn to_opt_string_lossy(s: *const libc::c_char) -> Option<String> {
pub(crate) fn as_path<'a>(s: *const libc::c_char) -> &'a std::path::Path { pub(crate) fn as_path<'a>(s: *const libc::c_char) -> &'a std::path::Path {
assert!(!s.is_null(), "cannot be used on null pointers"); assert!(!s.is_null(), "cannot be used on null pointers");
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
unsafe { let c_str = unsafe { std::ffi::CStr::from_ptr(s) }.to_bytes();
let c_str = std::ffi::CStr::from_ptr(s).to_bytes(); let os_str = std::ffi::OsStr::from_bytes(c_str);
let os_str = std::ffi::OsStr::from_bytes(c_str); std::path::Path::new(os_str)
std::path::Path::new(os_str)
}
} }
// as_path() implementation for windows, documented above. // as_path() implementation for windows, documented above.

View File

@@ -2,7 +2,7 @@
name = "deltachat-jsonrpc" name = "deltachat-jsonrpc"
version = "2.58.0-dev" version = "2.58.0-dev"
description = "DeltaChat JSON-RPC API" description = "DeltaChat JSON-RPC API"
edition = "2021" edition = "2024"
license = "MPL-2.0" license = "MPL-2.0"
repository = "https://github.com/chatmail/core" repository = "https://github.com/chatmail/core"

View File

@@ -5,26 +5,27 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use std::{collections::HashMap, str::FromStr}; use std::{collections::HashMap, str::FromStr};
use anyhow::{anyhow, bail, ensure, Context, Result}; use anyhow::{Context, Result, anyhow, bail, ensure};
use deltachat::EventEmitter;
pub use deltachat::accounts::Accounts; pub use deltachat::accounts::Accounts;
use deltachat::blob::BlobObject; use deltachat::blob::BlobObject;
use deltachat::calls::ice_servers; use deltachat::calls::ice_servers;
use deltachat::chat::{ use deltachat::chat::{
self, add_contact_to_chat, forward_msgs, forward_msgs_2ctx, get_chat_media, get_chat_msgs, self, Chat, ChatId, ChatItem, MessageListOptions, add_contact_to_chat, forward_msgs,
get_chat_msgs_ex, markfresh_chat, marknoticed_all_chats, marknoticed_chat, forward_msgs_2ctx, get_chat_media, get_chat_msgs, get_chat_msgs_ex, markfresh_chat,
remove_contact_from_chat, Chat, ChatId, ChatItem, MessageListOptions, marknoticed_all_chats, marknoticed_chat, remove_contact_from_chat,
}; };
use deltachat::chatlist::Chatlist; use deltachat::chatlist::Chatlist;
use deltachat::config::{get_all_ui_config_keys, Config}; use deltachat::config::{Config, get_all_ui_config_keys};
use deltachat::constants::DC_MSG_ID_DAYMARKER; use deltachat::constants::DC_MSG_ID_DAYMARKER;
use deltachat::contact::{may_be_valid_addr, Contact, ContactId, Origin}; use deltachat::contact::{Contact, ContactId, Origin, may_be_valid_addr};
use deltachat::context::get_info; use deltachat::context::get_info;
use deltachat::ephemeral::Timer; use deltachat::ephemeral::Timer;
use deltachat::imex; use deltachat::imex;
use deltachat::location; use deltachat::location;
use deltachat::message::{ use deltachat::message::{
self, delete_msgs_ex, get_existing_msg_ids, get_msg_read_receipt_count, get_msg_read_receipts, self, Message, MessageState, MsgId, Viewtype, delete_msgs_ex, get_existing_msg_ids,
markseen_msgs, Message, MessageState, MsgId, Viewtype, get_msg_read_receipt_count, get_msg_read_receipts, markseen_msgs,
}; };
use deltachat::peer_channels::{ use deltachat::peer_channels::{
leave_webxdc_realtime, send_webxdc_realtime_advertisement, send_webxdc_realtime_data, leave_webxdc_realtime, send_webxdc_realtime_advertisement, send_webxdc_realtime_data,
@@ -36,10 +37,9 @@ use deltachat::securejoin;
use deltachat::stock_str::StockMessage; use deltachat::stock_str::StockMessage;
use deltachat::storage_usage::{get_blobdir_storage_usage, get_storage_usage}; use deltachat::storage_usage::{get_blobdir_storage_usage, get_storage_usage};
use deltachat::webxdc::StatusUpdateSerial; use deltachat::webxdc::StatusUpdateSerial;
use deltachat::EventEmitter;
use sanitize_filename::is_sanitized; use sanitize_filename::is_sanitized;
use tokio::fs; use tokio::fs;
use tokio::sync::{watch, Mutex, RwLock}; use tokio::sync::{Mutex, RwLock, watch};
use types::login_param::EnteredLoginParam; use types::login_param::EnteredLoginParam;
use yerpc::rpc; use yerpc::rpc;
@@ -65,7 +65,7 @@ use self::types::{
JsonrpcMessageListItem, MessageNotificationInfo, MessageSearchResult, MessageViewtype, JsonrpcMessageListItem, MessageNotificationInfo, MessageSearchResult, MessageViewtype,
}, },
}; };
use crate::api::types::chat_list::{get_chat_list_item_by_id, ChatListItemFetchResult}; use crate::api::types::chat_list::{ChatListItemFetchResult, get_chat_list_item_by_id};
use crate::api::types::login_param::TransportListEntry; use crate::api::types::login_param::TransportListEntry;
use crate::api::types::qr::{QrObject, SecurejoinSource, SecurejoinUiPath}; use crate::api::types::qr::{QrObject, SecurejoinSource, SecurejoinUiPath};
@@ -2278,7 +2278,7 @@ impl CommandApi {
let message = Message::load_from_db(&ctx, MsgId::new(instance_msg_id)).await?; let message = Message::load_from_db(&ctx, MsgId::new(instance_msg_id)).await?;
let blob = message.get_webxdc_blob(&ctx, &path).await?; let blob = message.get_webxdc_blob(&ctx, &path).await?;
use base64::{engine::general_purpose, Engine as _}; use base64::{Engine as _, engine::general_purpose};
Ok(general_purpose::STANDARD_NO_PAD.encode(blob)) Ok(general_purpose::STANDARD_NO_PAD.encode(blob))
} }

View File

@@ -1,6 +1,6 @@
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use deltachat::calls::{call_state, CallState}; use deltachat::calls::{CallState, call_state};
use deltachat::context::Context; use deltachat::context::Context;
use deltachat::message::MsgId; use deltachat::message::MsgId;
use serde::Serialize; use serde::Serialize;

View File

@@ -1,7 +1,7 @@
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use anyhow::{bail, Context as _, Result}; use anyhow::{Context as _, Result, bail};
use deltachat::chat::{self, get_chat_contacts, get_past_chat_contacts, ChatVisibility}; use deltachat::chat::{self, ChatVisibility, get_chat_contacts, get_past_chat_contacts};
use deltachat::chat::{Chat, ChatId}; use deltachat::chat::{Chat, ChatId};
use deltachat::constants::Chattype; use deltachat::constants::Chattype;
use deltachat::contact::{Contact, ContactId}; use deltachat::contact::{Contact, ContactId};

View File

@@ -4,7 +4,7 @@ use deltachat::chatlist::get_last_message_for_chat;
use deltachat::constants::*; use deltachat::constants::*;
use deltachat::contact::Contact; use deltachat::contact::Contact;
use deltachat::{ use deltachat::{
chat::{get_chat_contacts, ChatVisibility}, chat::{ChatVisibility, get_chat_contacts},
chatlist::Chatlist, chatlist::Chatlist,
}; };
use num_traits::cast::ToPrimitive; use num_traits::cast::ToPrimitive;

View File

@@ -16,7 +16,7 @@ pub struct HttpResponse {
impl From<CoreHttpResponse> for HttpResponse { impl From<CoreHttpResponse> for HttpResponse {
fn from(response: CoreHttpResponse) -> Self { fn from(response: CoreHttpResponse) -> Self {
use base64::{engine::general_purpose, Engine as _}; use base64::{Engine as _, engine::general_purpose};
let blob = general_purpose::STANDARD_NO_PAD.encode(response.blob); let blob = general_purpose::STANDARD_NO_PAD.encode(response.blob);
let mimetype = response.mimetype; let mimetype = response.mimetype;
let encoding = response.encoding; let encoding = response.encoding;

View File

@@ -2,7 +2,7 @@
name = "ratelimit" name = "ratelimit"
version = "1.0.0" version = "1.0.0"
description = "Token bucket implementation" description = "Token bucket implementation"
edition = "2021" edition = "2024"
license = "MPL-2.0" license = "MPL-2.0"
[dependencies] [dependencies]

View File

@@ -2,7 +2,7 @@
name = "deltachat-repl" name = "deltachat-repl"
version = "2.58.0-dev" version = "2.58.0-dev"
license = "MPL-2.0" license = "MPL-2.0"
edition = "2021" edition = "2024"
repository = "https://github.com/chatmail/core" repository = "https://github.com/chatmail/core"
[dependencies] [dependencies]

View File

@@ -5,7 +5,7 @@ use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
use anyhow::{bail, ensure, Result}; use anyhow::{Result, bail, ensure};
use deltachat::chat::{self, Chat, ChatId, ChatItem, ChatVisibility, MuteDuration}; use deltachat::chat::{self, Chat, ChatId, ChatItem, ChatVisibility, MuteDuration};
use deltachat::chatlist::*; use deltachat::chatlist::*;
use deltachat::config; use deltachat::config;
@@ -1206,12 +1206,11 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
"fileinfo" => { "fileinfo" => {
ensure!(!arg1.is_empty(), "Argument <file> missing."); ensure!(!arg1.is_empty(), "Argument <file> missing.");
if let Ok(buf) = read_file(&context, Path::new(arg1)).await { let Ok(buf) = read_file(&context, Path::new(arg1)).await else {
let (width, height) = get_filemeta(&buf)?;
println!("width={width}, height={height}");
} else {
bail!("Command failed."); bail!("Command failed.");
} };
let (width, height) = get_filemeta(&buf)?;
println!("width={width}, height={height}");
} }
"estimatedeletion" => { "estimatedeletion" => {
ensure!(!arg1.is_empty(), "Argument <seconds> missing"); ensure!(!arg1.is_empty(), "Argument <seconds> missing");

View File

@@ -9,12 +9,12 @@ extern crate deltachat;
use std::borrow::Cow::{self, Borrowed, Owned}; use std::borrow::Cow::{self, Borrowed, Owned};
use anyhow::{bail, Error}; use anyhow::{Error, bail};
use deltachat::EventType;
use deltachat::chat::ChatId; use deltachat::chat::ChatId;
use deltachat::context::*; use deltachat::context::*;
use deltachat::qr_code_generator::get_securejoin_qr_svg; use deltachat::qr_code_generator::get_securejoin_qr_svg;
use deltachat::securejoin::*; use deltachat::securejoin::*;
use deltachat::EventType;
use log::{error, info, warn}; use log::{error, info, warn};
use nu_ansi_term::Color; use nu_ansi_term::Color;
use rustyline::completion::{Completer, FilenameCompleter, Pair}; use rustyline::completion::{Completer, FilenameCompleter, Pair};
@@ -266,10 +266,11 @@ impl Hinter for DcHelper {
&CONTACT_COMMANDS[..], &CONTACT_COMMANDS[..],
&MISC_COMMANDS[..], &MISC_COMMANDS[..],
] { ] {
if let Some(entry) = cmds.iter().find(|el| el.starts_with(&line[..pos])) { if let Some(entry) = cmds.iter().find(|el| el.starts_with(&line[..pos]))
if *entry != line && *entry != &line[..pos] { && *entry != line
return Some(entry[pos..].to_owned()); && *entry != &line[..pos]
} {
return Some(entry[pos..].to_owned());
} }
} }
} }

View File

@@ -2,7 +2,7 @@
name = "deltachat-rpc-server" name = "deltachat-rpc-server"
version = "2.58.0-dev" version = "2.58.0-dev"
description = "DeltaChat JSON-RPC server" description = "DeltaChat JSON-RPC server"
edition = "2021" edition = "2024"
readme = "README.md" readme = "README.md"
license = "MPL-2.0" license = "MPL-2.0"

View File

@@ -6,7 +6,7 @@ use std::env;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use anyhow::{anyhow, Context as _, Result}; use anyhow::{Context as _, Result, anyhow};
use deltachat::constants::DC_VERSION_STR; use deltachat::constants::DC_VERSION_STR;
use deltachat_jsonrpc::api::{Accounts, CommandApi}; use deltachat_jsonrpc::api::{Accounts, CommandApi};
use futures_lite::stream::StreamExt; use futures_lite::stream::StreamExt;

View File

@@ -2,7 +2,7 @@
name = "deltachat-time" name = "deltachat-time"
version = "1.0.0" version = "1.0.0"
description = "Time-related tools" description = "Time-related tools"
edition = "2021" edition = "2024"
license = "MPL-2.0" license = "MPL-2.0"
[dependencies] [dependencies]

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "deltachat_derive" name = "deltachat_derive"
version = "2.0.0" version = "2.0.0"
edition = "2018" edition = "2024"
license = "MPL-2.0" license = "MPL-2.0"
[lib] [lib]

View File

@@ -14,7 +14,7 @@ pub fn to_sql_derive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap(); let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident; let name = &ast.ident;
let gen = quote! { let q = quote! {
impl rusqlite::types::ToSql for #name { impl rusqlite::types::ToSql for #name {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> { fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
let num = *self as i64; let num = *self as i64;
@@ -24,7 +24,7 @@ pub fn to_sql_derive(input: TokenStream) -> TokenStream {
} }
} }
}; };
gen.into() q.into()
} }
#[proc_macro_derive(FromSql)] #[proc_macro_derive(FromSql)]
@@ -32,7 +32,7 @@ pub fn from_sql_derive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap(); let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident; let name = &ast.ident;
let gen = quote! { let q = quote! {
impl rusqlite::types::FromSql for #name { impl rusqlite::types::FromSql for #name {
fn column_result(col: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> { fn column_result(col: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
let inner = rusqlite::types::FromSql::column_result(col)?; let inner = rusqlite::types::FromSql::column_result(col)?;
@@ -44,5 +44,5 @@ pub fn from_sql_derive(input: TokenStream) -> TokenStream {
} }
} }
}; };
gen.into() q.into()
} }

View File

@@ -2,7 +2,7 @@
name = "format-flowed" name = "format-flowed"
version = "1.0.0" version = "1.0.0"
description = "format=flowed support" description = "format=flowed support"
edition = "2021" edition = "2024"
license = "MPL-2.0" license = "MPL-2.0"
keywords = ["email"] keywords = ["email"]

View File

@@ -78,11 +78,11 @@ pub fn format_flowed(text: &str) -> String {
let mut prefix = prefix.to_string(); let mut prefix = prefix.to_string();
if quote_depth > 0 { if quote_depth > 0
if let Some(s) = line.strip_prefix(' ') { && let Some(s) = line.strip_prefix(' ')
line = s; {
prefix += " "; line = s;
} prefix += " ";
} }
result += &format_line_flowed(line, &prefix); result += &format_line_flowed(line, &prefix);
@@ -224,8 +224,7 @@ mod tests {
fn test_unformat_flowed() { fn test_unformat_flowed() {
let text = "this is a very long message that should be wrapped using format=flowed and \n\ let text = "this is a very long message that should be wrapped using format=flowed and \n\
unwrapped on the receiver"; unwrapped on the receiver";
let expected = let expected = "this is a very long message that should be wrapped using format=flowed and \
"this is a very long message that should be wrapped using format=flowed and \
unwrapped on the receiver"; unwrapped on the receiver";
assert_eq!(unformat_flowed(text, false), expected); assert_eq!(unformat_flowed(text, false), expected);
@@ -255,8 +254,7 @@ mod tests {
assert_eq!(format_flowed_quote(quote), expected); assert_eq!(format_flowed_quote(quote), expected);
let quote = "this is a very long quote that should be wrapped using format=flowed and unwrapped on the receiver"; let quote = "this is a very long quote that should be wrapped using format=flowed and unwrapped on the receiver";
let expected = let expected = "> this is a very long quote that should be wrapped using format=flowed and \r\n\
"> this is a very long quote that should be wrapped using format=flowed and \r\n\
> unwrapped on the receiver"; > unwrapped on the receiver";
assert_eq!(format_flowed_quote(quote), expected); assert_eq!(format_flowed_quote(quote), expected);
} }

View File

@@ -2,7 +2,7 @@
name = "deltachat-fuzz" name = "deltachat-fuzz"
version = "0.0.0" version = "0.0.0"
publish = false publish = false
edition = "2021" edition = "2024"
license = "MPL-2.0" license = "MPL-2.0"
[dev-dependencies] [dev-dependencies]