From 47b94f3f562b0f6b3335db3948f89f4a022d4bfc Mon Sep 17 00:00:00 2001 From: Hocuri Date: Sun, 26 Apr 2026 09:43:51 +0200 Subject: [PATCH] refactor: Use regular functions rather than FromStr impls Implementing `FromStr` and then calling `parse()` creates an indirection, which is hard to follow for people who are not familiar with Rust. r10s recently had this problem. --- src/aheader.rs | 28 ++++++++++++---------------- src/ephemeral.rs | 15 ++++++--------- src/mimeparser.rs | 2 +- src/receive_imf.rs | 2 +- 4 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/aheader.rs b/src/aheader.rs index c82ce545b..218d08a58 100644 --- a/src/aheader.rs +++ b/src/aheader.rs @@ -4,9 +4,8 @@ use std::collections::BTreeMap; use std::fmt; -use std::str::FromStr; -use anyhow::{Context as _, Error, Result, bail}; +use anyhow::{Context as _, Result, bail}; use crate::key::{DcKey, SignedPublicKey}; @@ -28,10 +27,8 @@ impl fmt::Display for EncryptPreference { } } -impl FromStr for EncryptPreference { - type Err = Error; - - fn from_str(s: &str) -> Result { +impl EncryptPreference { + fn new(s: &str) -> Result { match s { "mutual" => Ok(EncryptPreference::Mutual), "nopreference" => Ok(EncryptPreference::NoPreference), @@ -85,10 +82,8 @@ impl fmt::Display for Aheader { } } -impl FromStr for Aheader { - type Err = Error; - - fn from_str(s: &str) -> Result { +impl Aheader { + pub(crate) fn from_str(s: &str) -> Result { let mut attributes: BTreeMap = s .split(';') .filter_map(|a| { @@ -116,7 +111,7 @@ impl FromStr for Aheader { let prefer_encrypt = attributes .remove("prefer-encrypt") - .and_then(|raw| raw.parse().ok()) + .and_then(|raw| EncryptPreference::new(&raw).ok()) .unwrap_or_default(); let verified = attributes.remove("_verified").is_some(); @@ -144,8 +139,9 @@ mod tests { #[test] fn test_from_str() -> Result<()> { - let h: Aheader = - format!("addr=me@mail.com; prefer-encrypt=mutual; keydata={RAWKEY}").parse()?; + let h = Aheader::from_str(&format!( + "addr=me@mail.com; prefer-encrypt=mutual; keydata={RAWKEY}" + ))?; assert_eq!(h.addr, "me@mail.com"); assert_eq!(h.prefer_encrypt, EncryptPreference::Mutual); @@ -157,7 +153,7 @@ mod tests { #[test] fn test_from_str_reset() -> Result<()> { let raw = format!("addr=reset@example.com; prefer-encrypt=reset; keydata={RAWKEY}"); - let h: Aheader = raw.parse()?; + let h = Aheader::from_str(&raw)?; assert_eq!(h.addr, "reset@example.com"); assert_eq!(h.prefer_encrypt, EncryptPreference::NoPreference); @@ -167,7 +163,7 @@ mod tests { #[test] fn test_from_str_non_critical() -> Result<()> { let raw = format!("addr=me@mail.com; _foo=one; _bar=two; keydata={RAWKEY}"); - let h: Aheader = raw.parse()?; + let h = Aheader::from_str(&raw)?; assert_eq!(h.addr, "me@mail.com"); assert_eq!(h.prefer_encrypt, EncryptPreference::NoPreference); @@ -177,7 +173,7 @@ mod tests { #[test] fn test_from_str_superflous_critical() { let raw = format!("addr=me@mail.com; _foo=one; _bar=two; other=me; keydata={RAWKEY}"); - assert!(raw.parse::().is_err()); + assert!(Aheader::from_str(&raw).is_err()); } #[test] diff --git a/src/ephemeral.rs b/src/ephemeral.rs index 42bf9cb28..c546b72d7 100644 --- a/src/ephemeral.rs +++ b/src/ephemeral.rs @@ -66,7 +66,6 @@ use std::cmp::max; use std::collections::BTreeSet; use std::fmt; use std::num::ParseIntError; -use std::str::FromStr; use std::time::{Duration, UNIX_EPOCH}; use anyhow::{Context as _, Result, ensure}; @@ -124,6 +123,12 @@ impl Timer { Self::Enabled { duration } } } + + /// Tries to parse a string as an integer, + /// and converts it to an ephemeral timer value. + pub fn from_str(input: &str) -> Result { + input.parse::().map(Self::from_u32) + } } impl fmt::Display for Timer { @@ -132,14 +137,6 @@ impl fmt::Display for Timer { } } -impl FromStr for Timer { - type Err = ParseIntError; - - fn from_str(input: &str) -> Result { - input.parse::().map(Self::from_u32) - } -} - impl rusqlite::types::ToSql for Timer { fn to_sql(&self) -> rusqlite::Result> { let val = rusqlite::types::Value::Integer(match self { diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 04e618a85..36dfc2f78 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -2127,7 +2127,7 @@ async fn parse_gossip_headers( let mut gossiped_keys: BTreeMap = Default::default(); for value in &gossip_headers { - let header = match value.parse::() { + let header = match Aheader::from_str(value) { Ok(header) => header, Err(err) => { warn!(context, "Failed parsing Autocrypt-Gossip header: {}", err); diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 443c9dc37..195fc0cdc 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -1873,7 +1873,7 @@ async fn add_parts( // Extract ephemeral timer from the message let mut ephemeral_timer = if let Some(value) = mime_parser.get_header(HeaderDef::EphemeralTimer) { - match value.parse::() { + match EphemeralTimer::from_str(value) { Ok(timer) => timer, Err(err) => { warn!(context, "Can't parse ephemeral timer \"{value}\": {err:#}.");