From cee901bede745b6d22127b7085af1f4021ac51ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Tue, 29 Sep 2026 09:42:28 +0200 Subject: [PATCH] refactor: make imap-types no_std compatible Refs: #495 --- CHANGELOG.md | 1 + imap-types/Cargo.toml | 4 +- imap-types/src/arbitrary.rs | 4 ++ imap-types/src/auth.rs | 8 +++- imap-types/src/body.rs | 2 + imap-types/src/command.rs | 9 +++-- imap-types/src/core.rs | 37 ++++++++++++------- imap-types/src/datetime.rs | 10 +++-- imap-types/src/envelope.rs | 2 + imap-types/src/error.rs | 4 +- imap-types/src/extensions/binary.rs | 10 ++--- imap-types/src/extensions/compress.rs | 4 +- .../src/extensions/condstore_qresync.rs | 6 +-- imap-types/src/extensions/enable.rs | 4 +- imap-types/src/extensions/namespace.rs | 2 + imap-types/src/extensions/quota.rs | 8 ++-- imap-types/src/extensions/sort.rs | 4 +- imap-types/src/extensions/thread.rs | 9 +++-- imap-types/src/extensions/uidplus.rs | 2 +- imap-types/src/extensions/utf8.rs | 12 +++--- imap-types/src/fetch.rs | 7 ++-- imap-types/src/flag.rs | 6 +-- imap-types/src/lib.rs | 6 +++ imap-types/src/mailbox.rs | 7 +++- imap-types/src/response.rs | 21 +++++++---- imap-types/src/search.rs | 3 +- imap-types/src/secret.rs | 7 +++- imap-types/src/sequence.rs | 13 +++++-- imap-types/src/status.rs | 2 +- imap-types/src/utils.rs | 4 +- 30 files changed, 136 insertions(+), 82 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 977a9feb..78423139 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added short `README.md` to `assets` folder * Added quirk for trailing space in STATUS. Thanks, @nbdd0121! * Increased MSRV to 1.78. +* Made imap-types `no_std` (requires `alloc`), unless `arbitrary` or `tag_generator` are enabled ### Changed diff --git a/imap-types/Cargo.toml b/imap-types/Cargo.toml index 6412867c..33fda4a1 100644 --- a/imap-types/Cargo.toml +++ b/imap-types/Cargo.toml @@ -38,8 +38,8 @@ bounded-static-derive = { version = "0.8.0", default-features = false } bounded-static = { version = "0.8.0", default-features = false, features = ["alloc"] } chrono = { version = "0.4", default-features = false } rand = { version = "0.10", default-features = false, features = ["thread_rng"], optional = true } -serde = { version = "1.0.228", features = ["derive"], optional = true } -thiserror = "2.0.18" +serde = { version = "1.0.228", default-features = false, features = ["alloc", "derive"], optional = true } +thiserror = { version = "2.0.18", default-features = false } [dev-dependencies] criterion = { version = "0.7.0", default-features = false } diff --git a/imap-types/src/arbitrary.rs b/imap-types/src/arbitrary.rs index 9ae3055b..0cc46abb 100644 --- a/imap-types/src/arbitrary.rs +++ b/imap-types/src/arbitrary.rs @@ -1,3 +1,7 @@ +#[cfg(not(feature = "arbitrary_simplified"))] +use alloc::boxed::Box; +use alloc::vec::Vec; + use arbitrary::{Arbitrary, Unstructured}; use chrono::{FixedOffset, TimeZone}; diff --git a/imap-types/src/auth.rs b/imap-types/src/auth.rs index 3a7ed8a3..febe448f 100644 --- a/imap-types/src/auth.rs +++ b/imap-types/src/auth.rs @@ -1,7 +1,11 @@ //! Authentication-related types. -use std::{ +use alloc::{ borrow::Cow, + string::{String, ToString}, + vec::Vec, +}; +use core::{ fmt::{Display, Formatter}, str::FromStr, }; @@ -163,7 +167,7 @@ impl<'a> From> for AuthMechanism<'a> { } impl Display for AuthMechanism<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { f.write_str(self.as_ref()) } } diff --git a/imap-types/src/body.rs b/imap-types/src/body.rs index 0125d5ae..4685407b 100644 --- a/imap-types/src/body.rs +++ b/imap-types/src/body.rs @@ -1,5 +1,7 @@ //! Body(structure)-related types. +use alloc::{boxed::Box, vec::Vec}; + #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; diff --git a/imap-types/src/command.rs b/imap-types/src/command.rs index 3ea25860..17f0471c 100644 --- a/imap-types/src/command.rs +++ b/imap-types/src/command.rs @@ -2,11 +2,11 @@ //! //! See . -use std::borrow::Cow; +use alloc::{borrow::Cow, vec::Vec}; #[cfg(feature = "ext_condstore_qresync")] -use std::num::NonZeroU32; +use core::num::NonZeroU32; #[cfg(feature = "ext_condstore_qresync")] -use std::num::NonZeroU64; +use core::num::NonZeroU64; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; @@ -1933,6 +1933,9 @@ pub mod error { #[cfg(test)] mod tests { + use alloc::{boxed::Box, format, vec}; + use std::println; + use chrono::DateTime as ChronoDateTime; use super::*; diff --git a/imap-types/src/core.rs b/imap-types/src/core.rs index 28a8ae93..4f4c59e3 100644 --- a/imap-types/src/core.rs +++ b/imap-types/src/core.rs @@ -35,12 +35,18 @@ //! ``` #[cfg(feature = "tag_generator")] -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::{ +use alloc::format; +use alloc::{ borrow::Cow, + string::String, + vec, + vec::{IntoIter, Vec}, +}; +#[cfg(feature = "tag_generator")] +use core::sync::atomic::{AtomicUsize, Ordering}; +use core::{ fmt::{Debug, Display, Formatter}, str::from_utf8, - vec::IntoIter, }; #[cfg(feature = "arbitrary")] @@ -113,7 +119,7 @@ pub struct Atom<'a>(pub(crate) Cow<'a, str>); // We want a slightly more dense `Debug` implementation. impl Debug for Atom<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { write!(f, "Atom({:?})", self.0) } } @@ -228,7 +234,7 @@ impl AsRef for Atom<'_> { } impl Display for Atom<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { write!(f, "{}", self.0) } } @@ -263,7 +269,7 @@ pub struct AtomExt<'a>(pub(crate) Cow<'a, str>); // We want a slightly more dense `Debug` implementation. impl Debug for AtomExt<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { write!(f, "AtomExt({:?})", self.0) } } @@ -543,11 +549,11 @@ where // We want a more readable `Debug` implementation. impl Debug for Literal<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { struct BStr<'a>(&'a Cow<'a, [u8]>); impl Debug for BStr<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!( f, "b\"{}\"", @@ -763,7 +769,7 @@ pub enum LiteralMode { pub struct Quoted<'a>(pub(crate) Cow<'a, str>); impl Debug for Quoted<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { write!(f, "Quoted({:?})", self.0) } } @@ -1061,7 +1067,7 @@ pub struct Tag<'a>(pub(crate) Cow<'a, str>); // We want a slightly more dense `Debug` implementation. impl Debug for Tag<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { write!(f, "Tag({:?})", self.0) } } @@ -1233,13 +1239,13 @@ pub struct Text<'a>(pub(crate) Cow<'a, str>); // We want a slightly more dense `Debug` implementation. impl Debug for Text<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { write!(f, "Text({:?})", self.0) } } impl Display for Text<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { write!(f, "{}", self.0.as_ref()) } } @@ -1534,7 +1540,7 @@ impl Debug for VecN where T: Debug, { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { self.0.fmt(f)?; match N { 0 => write!(f, "*"), @@ -1631,7 +1637,10 @@ impl From<(T, T)> for Vec2 { #[cfg(test)] mod tests { - use std::str::from_utf8; + use alloc::borrow::ToOwned; + #[cfg(feature = "serde")] + use alloc::string::ToString; + use core::str::from_utf8; #[cfg(feature = "tag_generator")] use std::{collections::BTreeSet, thread, time::Duration}; diff --git a/imap-types/src/datetime.rs b/imap-types/src/datetime.rs index f4baaa05..416a33db 100644 --- a/imap-types/src/datetime.rs +++ b/imap-types/src/datetime.rs @@ -1,6 +1,6 @@ //! Date and time-related types. -use std::fmt::{Debug, Formatter}; +use core::fmt::{Debug, Formatter}; use bounded_static::{IntoBoundedStatic, ToBoundedStatic}; use chrono::{Datelike, FixedOffset}; @@ -64,7 +64,7 @@ impl TryFrom> for DateTime { } impl Debug for DateTime { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { Debug::fmt(&self.0, f) } } @@ -134,7 +134,7 @@ impl TryFrom for NaiveDate { } impl Debug for NaiveDate { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { Debug::fmt(&self.0, f) } } @@ -184,6 +184,10 @@ pub mod error { #[cfg(test)] mod tests { + #[cfg(feature = "serde")] + use alloc::string::ToString; + use std::println; + use chrono::{TimeZone, Timelike}; use super::*; diff --git a/imap-types/src/envelope.rs b/imap-types/src/envelope.rs index ca3af7dc..6073a16f 100644 --- a/imap-types/src/envelope.rs +++ b/imap-types/src/envelope.rs @@ -1,5 +1,7 @@ //! Envelope-related types. +use alloc::vec::Vec; + #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; diff --git a/imap-types/src/error.rs b/imap-types/src/error.rs index 5aecf3a1..ea28fcd0 100644 --- a/imap-types/src/error.rs +++ b/imap-types/src/error.rs @@ -1,6 +1,6 @@ //! Error-related types. -use std::fmt::{Display, Formatter}; +use core::fmt::{Display, Formatter}; use thiserror::Error; @@ -13,7 +13,7 @@ pub struct ValidationError { } impl Display for ValidationError { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { write!(f, "Validation failed: {}", self.kind) } } diff --git a/imap-types/src/extensions/binary.rs b/imap-types/src/extensions/binary.rs index 0c397a2c..633d37aa 100644 --- a/imap-types/src/extensions/binary.rs +++ b/imap-types/src/extensions/binary.rs @@ -1,9 +1,7 @@ //! IMAP4 Binary Content Extension -use std::{ - borrow::Cow, - fmt::{Debug, Formatter}, -}; +use alloc::borrow::Cow; +use core::fmt::{Debug, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; @@ -41,11 +39,11 @@ pub struct Literal8<'a> { // We want a more readable `Debug` implementation. impl Debug for Literal8<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { struct BStr<'a>(&'a Cow<'a, [u8]>); impl Debug for BStr<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!( f, "b\"{}\"", diff --git a/imap-types/src/extensions/compress.rs b/imap-types/src/extensions/compress.rs index 1ec3bd6b..5d25bd67 100644 --- a/imap-types/src/extensions/compress.rs +++ b/imap-types/src/extensions/compress.rs @@ -10,7 +10,7 @@ //! * the [`Command`](crate::command::Command) enum with a new variant [`Command::Compress`](crate::command::Command#variant.Compress), and //! * the [`Code`](crate::response::Code) enum with a new variant [`Code::CompressionActive`](crate::response::Code#variant.CompressionActive). -use std::fmt::{Display, Formatter}; +use core::fmt::{Display, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; @@ -43,7 +43,7 @@ pub enum CompressionAlgorithm { } impl Display for CompressionAlgorithm { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { f.write_str(match self { Self::Deflate => "DEFLATE", }) diff --git a/imap-types/src/extensions/condstore_qresync.rs b/imap-types/src/extensions/condstore_qresync.rs index 2aeb7b84..af02655d 100644 --- a/imap-types/src/extensions/condstore_qresync.rs +++ b/imap-types/src/extensions/condstore_qresync.rs @@ -1,4 +1,4 @@ -use std::fmt::{Display, Formatter}; +use core::fmt::{Display, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; @@ -54,7 +54,7 @@ impl<'a> TryFrom<&'a str> for AttributeFlag<'a> { } impl Display for AttributeFlag<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { match self { AttributeFlag::Answered => f.write_str("\\\\Answered"), AttributeFlag::Flagged => f.write_str("\\\\Flagged"), @@ -77,7 +77,7 @@ pub enum EntryTypeReq { } impl Display for EntryTypeReq { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { match self { EntryTypeReq::Private => write!(f, "priv"), EntryTypeReq::Shared => write!(f, "shared"), diff --git a/imap-types/src/extensions/enable.rs b/imap-types/src/extensions/enable.rs index 88e5c0e5..b3b5c6c2 100644 --- a/imap-types/src/extensions/enable.rs +++ b/imap-types/src/extensions/enable.rs @@ -6,7 +6,7 @@ //! * the [CommandBody] enum with a new variant [CommandBody::Enable], and //! * the [Data](crate::response::Data) enum with a new variant [Data::Enabled](crate::response::Data#variant.Enabled). -use std::fmt::{Display, Formatter}; +use core::fmt::{Display, Formatter}; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] @@ -79,7 +79,7 @@ impl<'a> From> for CapabilityEnable<'a> { } impl Display for CapabilityEnable<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { match self { #[cfg(feature = "ext_condstore_qresync")] Self::CondStore => write!(f, "CONDSTORE"), diff --git a/imap-types/src/extensions/namespace.rs b/imap-types/src/extensions/namespace.rs index 788a3b9e..74374579 100644 --- a/imap-types/src/extensions/namespace.rs +++ b/imap-types/src/extensions/namespace.rs @@ -14,6 +14,8 @@ //! //! - [`Data::Namespace`] +use alloc::vec::Vec; + #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; diff --git a/imap-types/src/extensions/quota.rs b/imap-types/src/extensions/quota.rs index 6fd962bd..2b21b006 100644 --- a/imap-types/src/extensions/quota.rs +++ b/imap-types/src/extensions/quota.rs @@ -33,10 +33,8 @@ //! - [`StatusDataItem::Deleted`](crate::status::StatusDataItem::Deleted) //! - [`StatusDataItem::DeletedStorage`](crate::status::StatusDataItem::DeletedStorage) -use std::{ - borrow::Cow, - fmt::{Display, Formatter}, -}; +use alloc::{borrow::Cow, string::String, vec::Vec}; +use core::fmt::{Display, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; @@ -207,7 +205,7 @@ impl<'a> From> for Resource<'a> { } impl Display for Resource<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { f.write_str(match self { Self::Storage => "STORAGE", Self::Message => "MESSAGE", diff --git a/imap-types/src/extensions/sort.rs b/imap-types/src/extensions/sort.rs index 08cf1556..bbb85352 100644 --- a/imap-types/src/extensions/sort.rs +++ b/imap-types/src/extensions/sort.rs @@ -1,4 +1,4 @@ -use std::fmt::{Display, Formatter}; +use core::fmt::{Display, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; @@ -28,7 +28,7 @@ impl<'a> From> for SortAlgorithm<'a> { } impl Display for SortAlgorithm<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { match self { SortAlgorithm::Display => f.write_str("DISPLAY"), SortAlgorithm::Other(other) => f.write_str(other.as_ref()), diff --git a/imap-types/src/extensions/thread.rs b/imap-types/src/extensions/thread.rs index af54d235..3d8a27c0 100644 --- a/imap-types/src/extensions/thread.rs +++ b/imap-types/src/extensions/thread.rs @@ -1,4 +1,5 @@ -use std::{ +use alloc::{vec, vec::Vec}; +use core::{ fmt::{Display, Formatter}, num::NonZeroU32, }; @@ -27,7 +28,7 @@ pub enum Thread { } impl Display for Thread { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { let empty_answers: Vec = vec![]; write!(f, "(")?; @@ -78,7 +79,7 @@ impl Display for Thread { } } -fn write_prefix(f: &mut Formatter, prefix: &Vec1) -> std::fmt::Result { +fn write_prefix(f: &mut Formatter, prefix: &Vec1) -> core::fmt::Result { let (head, tail) = prefix.as_ref().split_first().unwrap(); write!(f, "{head}")?; @@ -156,7 +157,7 @@ impl<'a> From> for ThreadingAlgorithm<'a> { } impl Display for ThreadingAlgorithm<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { f.write_str(match self { ThreadingAlgorithm::OrderedSubject => "ORDEREDSUBJECT", ThreadingAlgorithm::References => "REFERENCES", diff --git a/imap-types/src/extensions/uidplus.rs b/imap-types/src/extensions/uidplus.rs index 6e875754..8426c0b3 100644 --- a/imap-types/src/extensions/uidplus.rs +++ b/imap-types/src/extensions/uidplus.rs @@ -1,4 +1,4 @@ -use std::num::NonZeroU32; +use core::num::NonZeroU32; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; diff --git a/imap-types/src/extensions/utf8.rs b/imap-types/src/extensions/utf8.rs index a3d7acac..88e2259f 100644 --- a/imap-types/src/extensions/utf8.rs +++ b/imap-types/src/extensions/utf8.rs @@ -1,7 +1,5 @@ -use std::{ - borrow::Cow, - fmt::{Debug, Display, Formatter}, -}; +use alloc::{borrow::Cow, string::String}; +use core::fmt::{Debug, Display, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; @@ -22,7 +20,7 @@ pub enum Utf8Kind { } impl Display for Utf8Kind { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { f.write_str(match self { Self::Accept => "ACCEPT", Self::Only => "ONLY", @@ -42,7 +40,7 @@ impl Display for Utf8Kind { pub struct QuotedUtf8<'a>(pub(crate) Cow<'a, str>); impl Debug for QuotedUtf8<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { write!(f, "QuotedUtf8({:?})", self.0) } } @@ -122,7 +120,7 @@ impl AsRef for QuotedUtf8<'_> { #[cfg(test)] mod tests { - use std::borrow::Cow; + use alloc::borrow::{Cow, ToOwned}; use super::QuotedUtf8; use crate::error::{ValidationError, ValidationErrorKind}; diff --git a/imap-types/src/fetch.rs b/imap-types/src/fetch.rs index 83ee855e..a76dc59d 100644 --- a/imap-types/src/fetch.rs +++ b/imap-types/src/fetch.rs @@ -1,8 +1,9 @@ //! Fetch-related types. +use alloc::{vec, vec::Vec}; #[cfg(feature = "ext_condstore_qresync")] -use std::num::NonZeroU64; -use std::{ +use core::num::NonZeroU64; +use core::{ fmt::{Display, Formatter}, num::NonZeroU32, }; @@ -49,7 +50,7 @@ impl Macro { } impl Display for Macro { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { f.write_str(match self { Macro::All => "ALL", Macro::Fast => "FAST", diff --git a/imap-types/src/flag.rs b/imap-types/src/flag.rs index fcf7b18a..b2898893 100644 --- a/imap-types/src/flag.rs +++ b/imap-types/src/flag.rs @@ -1,6 +1,6 @@ //! Flag-related types. -use std::fmt::{Display, Formatter}; +use core::fmt::{Display, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; @@ -79,7 +79,7 @@ impl<'a> TryFrom<&'a str> for Flag<'a> { } impl Display for Flag<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { match self { Flag::Answered => f.write_str("\\Answered"), Flag::Deleted => f.write_str("\\Deleted"), @@ -191,7 +191,7 @@ impl<'a> From> for FlagNameAttribute<'a> { } impl Display for FlagNameAttribute<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { match self { Self::Noinferiors => f.write_str("\\Noinferiors"), Self::Noselect => f.write_str("\\Noselect"), diff --git a/imap-types/src/lib.rs b/imap-types/src/lib.rs index 7ab3be3f..9185e021 100644 --- a/imap-types/src/lib.rs +++ b/imap-types/src/lib.rs @@ -142,6 +142,7 @@ //! (See, e.g., `imap-types/fuzz/fuzz_targets/to_static.rs`) //! When the `serde` feature is used, all types implement [Serde](https://serde.rs/)'s [Serialize](https://docs.serde.rs/serde/trait.Serialize.html) and //! [Deserialize](https://docs.serde.rs/serde/trait.Deserialize.html) traits. (Try running `cargo run --example serde_json`.) +//! imap-types is `no_std` and requires `alloc`. The `arbitrary` and `tag_generator` features require `std`. //! Using `tag_generator` unlocks a `TagGenerator` to generate random tags. //! This may help to prevent attacks that depend on the knowledge of the next tag. //! @@ -168,6 +169,7 @@ //! [RFC 7888]: https://datatracker.ietf.org/doc/html/rfc7888 //! [RFC 9208]: https://datatracker.ietf.org/doc/html/rfc9208 +#![no_std] // TODO(#660) #![allow(unknown_lints)] #![allow(mismatched_lifetime_syntaxes)] @@ -177,6 +179,10 @@ // #![deny(missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg))] +extern crate alloc; +#[cfg(any(test, feature = "arbitrary", feature = "tag_generator"))] +extern crate std; + use bounded_static::{IntoBoundedStatic, ToBoundedStatic}; // Test examples from imap-types' README. diff --git a/imap-types/src/mailbox.rs b/imap-types/src/mailbox.rs index 8050d910..29446681 100644 --- a/imap-types/src/mailbox.rs +++ b/imap-types/src/mailbox.rs @@ -1,6 +1,7 @@ //! Mailbox-related types. -use std::{borrow::Cow, str::from_utf8}; +use alloc::{borrow::Cow, string::String, vec::Vec}; +use core::str::from_utf8; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; @@ -284,7 +285,9 @@ pub mod error { #[cfg(test)] mod tests { - use std::borrow::Cow; + use alloc::borrow::Cow; + #[cfg(feature = "serde")] + use alloc::string::ToString; use super::*; #[cfg(feature = "serde")] diff --git a/imap-types/src/response.rs b/imap-types/src/response.rs index af4a9b4b..c0862b2a 100644 --- a/imap-types/src/response.rs +++ b/imap-types/src/response.rs @@ -1,9 +1,13 @@ //! # 7. Server Responses +use alloc::{ + borrow::{Cow, ToOwned}, + string::String, + vec::Vec, +}; #[cfg(feature = "ext_condstore_qresync")] -use std::num::NonZeroU64; -use std::{ - borrow::Cow, +use core::num::NonZeroU64; +use core::{ fmt::{Debug, Display, Formatter}, num::{NonZeroU32, TryFromIntError}, }; @@ -723,7 +727,7 @@ struct CommandContinuationRequestBasicShadow<'a> { impl<'a> TryFrom> for CommandContinuationRequestBasic<'a> { - type Error = ContinueError; + type Error = ContinueError; fn try_from(value: CommandContinuationRequestBasicShadow<'a>) -> Result { Self::new(value.code, value.text) @@ -1006,11 +1010,11 @@ pub struct CodeOther<'a>(Cow<'a, [u8]>); // We want a more readable `Debug` implementation. impl Debug for CodeOther<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { struct BStr<'a>(&'a Cow<'a, [u8]>); impl Debug for BStr<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!( f, "b\"{}\"", @@ -1119,7 +1123,7 @@ pub enum Capability<'a> { } impl Display for Capability<'_> { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { match self { Self::Imap4Rev1 => write!(f, "IMAP4REV1"), Self::Auth(mechanism) => write!(f, "AUTH={mechanism}"), @@ -1318,6 +1322,9 @@ pub mod error { #[cfg(test)] mod tests { + use alloc::{string::ToString, vec}; + use std::println; + use super::*; #[test] diff --git a/imap-types/src/search.rs b/imap-types/src/search.rs index 886a3389..6386d650 100644 --- a/imap-types/src/search.rs +++ b/imap-types/src/search.rs @@ -1,7 +1,8 @@ //! Search-related types. +use alloc::boxed::Box; #[cfg(feature = "ext_within")] -use std::num::NonZeroU32; +use core::num::NonZeroU32; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] diff --git a/imap-types/src/secret.rs b/imap-types/src/secret.rs index eb512480..5b5f8187 100644 --- a/imap-types/src/secret.rs +++ b/imap-types/src/secret.rs @@ -3,7 +3,7 @@ //! This module provides a `Secret` ensuring that sensitive values are not //! `Debug`-printed by accident. -use std::fmt::{Debug, Formatter}; +use core::fmt::{Debug, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; @@ -39,7 +39,7 @@ impl Debug for Secret where T: Debug, { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { #[cfg(not(debug_assertions))] return write!(f, "/* REDACTED */"); #[cfg(debug_assertions)] @@ -58,6 +58,9 @@ mod tests { #[cfg(not(debug_assertions))] #[allow(clippy::redundant_clone)] fn test_that_secret_is_redacted() { + use alloc::{format, vec}; + use std::println; + use super::Secret; use crate::auth::{AuthMechanism, AuthenticateData}; diff --git a/imap-types/src/sequence.rs b/imap-types/src/sequence.rs index 73d70b0c..ced3a3f5 100644 --- a/imap-types/src/sequence.rs +++ b/imap-types/src/sequence.rs @@ -1,6 +1,10 @@ -use std::{ +use alloc::{ + collections::{BTreeSet, VecDeque}, + vec, + vec::Vec, +}; +use core::{ cmp::{Ordering, max}, - collections::{HashSet, VecDeque}, fmt::Debug, iter::Rev, mem, @@ -37,7 +41,7 @@ pub struct SequenceSet(pub Vec1); impl SequenceSet { pub fn normalize(&mut self) -> &mut Self { - let mut singles = HashSet::with_capacity(self.0.0.len()); + let mut singles = BTreeSet::new(); let mut ranges = Vec::with_capacity(self.0.0.len()); // First, normalize all sequences @@ -801,7 +805,8 @@ fn merge_ranges(mut ranges: Vec>) -> Vec> { #[cfg(test)] mod tests { - use std::num::NonZeroU32; + use core::num::NonZeroU32; + use std::{print, println}; use super::*; use crate::core::Vec1; diff --git a/imap-types/src/status.rs b/imap-types/src/status.rs index 5af6064b..cb4020e1 100644 --- a/imap-types/src/status.rs +++ b/imap-types/src/status.rs @@ -1,4 +1,4 @@ -use std::num::NonZeroU32; +use core::num::NonZeroU32; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; diff --git a/imap-types/src/utils.rs b/imap-types/src/utils.rs index 6acea9e6..f1c798f8 100644 --- a/imap-types/src/utils.rs +++ b/imap-types/src/utils.rs @@ -1,6 +1,6 @@ //! Functions that may come in handy. -use std::borrow::Cow; +use alloc::{borrow::Cow, format, string::String, vec::Vec}; /// Converts bytes into a ready-to-be-printed form. pub fn escape_byte_string(bytes: B) -> String @@ -139,6 +139,8 @@ pub fn unescape_quoted(escaped: &str) -> Cow<'_, str> { #[cfg(test)] mod tests { + use alloc::string::ToString; + use super::*; #[test]