imap 3.0.0-alpha.13

IMAP client for Rust
Documentation
//! Adds support for the IMAP SORT extension specificed in [RFC
//! 5464](https://tools.ietf.org/html/rfc5256#section-3).
//!
//! The SORT command is a variant of SEARCH with sorting semantics for
//! the results. There are two arguments before the searching
//! criteria argument: a parenthesized list of sort criteria, and the
//! searching charset.

use std::{borrow::Cow, fmt};

pub(crate) struct SortCriteria<'c>(pub(crate) &'c [SortCriterion<'c>]);

impl<'c> fmt::Display for SortCriteria<'c> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.0.is_empty() {
            write!(f, "")
        } else {
            let criteria: Vec<String> = self.0.iter().map(|c| c.to_string()).collect();
            write!(f, "({})", criteria.join(" "))
        }
    }
}

/// Message sorting preferences used for [`Session::sort`](crate::Session::sort)
/// and [`Session::uid_sort`](crate::Session::uid_sort).
///
/// Any sorting criterion that refers to an address (`From`, `To`, etc.) sorts according to the
/// "addr-mailbox" of the indicated address. You can find the formal syntax for addr-mailbox [in
/// the IMAP spec](https://tools.ietf.org/html/rfc3501#section-9), and a more detailed discussion
/// of the relevant semantics [in RFC 2822](https://tools.ietf.org/html/rfc2822#section-3.4.1).
/// Essentially, the address refers _either_ to the name of the contact _or_ to its local-part (the
/// left part of the email address, before the `@`).
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
pub enum SortCriterion<'c> {
    /// Internal date and time of the message. This differs from the
    /// ON criteria in SEARCH, which uses just the internal date.
    Arrival,

    /// IMAP addr-mailbox of the first "Cc" address.
    Cc,

    /// Sent date and time, as described in
    /// [section 2.2](https://tools.ietf.org/html/rfc5256#section-2.2).
    Date,

    /// IMAP addr-mailbox of the first "From" address.
    From,

    /// Followed by another sort criterion, has the effect of that
    /// criterion but in reverse (descending) order.
    Reverse(&'c SortCriterion<'c>),

    /// Size of the message in octets.
    Size,

    /// Base subject text.
    Subject,

    /// IMAP addr-mailbox of the first "To" address.
    To,
}

impl<'c> fmt::Display for SortCriterion<'c> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use SortCriterion::*;

        match self {
            Arrival => write!(f, "ARRIVAL"),
            Cc => write!(f, "CC"),
            Date => write!(f, "DATE"),
            From => write!(f, "FROM"),
            Reverse(c) => write!(f, "REVERSE {}", c),
            Size => write!(f, "SIZE"),
            Subject => write!(f, "SUBJECT"),
            To => write!(f, "TO"),
        }
    }
}

/// The character encoding to use for strings that are subject to a [`SortCriterion`].
///
/// Servers are only required to implement [`SortCharset::UsAscii`] and [`SortCharset::Utf8`].
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SortCharset<'c> {
    /// Strings are UTF-8 encoded.
    Utf8,

    /// Strings are encoded with ASCII.
    UsAscii,

    /// Strings are encoded using some other character set.
    ///
    /// Note that this option is subject to server support for the specified character set.
    Custom(Cow<'c, str>),
}

impl<'c> fmt::Display for SortCharset<'c> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use SortCharset::*;

        match self {
            Utf8 => write!(f, "UTF-8"),
            UsAscii => write!(f, "US-ASCII"),
            Custom(c) => write!(f, "{}", c),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_criterion_to_string() {
        use SortCriterion::*;

        assert_eq!("ARRIVAL", Arrival.to_string());
        assert_eq!("CC", Cc.to_string());
        assert_eq!("DATE", Date.to_string());
        assert_eq!("FROM", From.to_string());
        assert_eq!("SIZE", Size.to_string());
        assert_eq!("SUBJECT", Subject.to_string());
        assert_eq!("TO", To.to_string());
        assert_eq!("REVERSE TO", Reverse(&To).to_string());
        assert_eq!("REVERSE REVERSE TO", Reverse(&Reverse(&To)).to_string());
    }

    #[test]
    fn test_criteria_to_string() {
        use SortCriterion::*;

        assert_eq!("", SortCriteria(&[]).to_string());
        assert_eq!("(ARRIVAL)", SortCriteria(&[Arrival]).to_string());
        assert_eq!(
            "(ARRIVAL REVERSE FROM)",
            SortCriteria(&[Arrival, Reverse(&From)]).to_string()
        );
        assert_eq!(
            "(ARRIVAL REVERSE REVERSE REVERSE FROM)",
            SortCriteria(&[Arrival, Reverse(&Reverse(&Reverse(&From)))]).to_string()
        );
    }

    #[test]
    fn test_charset_to_string() {
        use SortCharset::*;

        assert_eq!("UTF-8", Utf8.to_string());
        assert_eq!("US-ASCII", UsAscii.to_string());
        assert_eq!("CHARSET", Custom("CHARSET".into()).to_string());
    }
}