rust-imap/src/extensions/sort.rs
2024-07-14 10:59:17 +02:00

154 lines
4.9 KiB
Rust

//! Adds support for the IMAP SORT extension specified 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());
}
}