imap_types/extensions/
compress.rs

1//! The IMAP COMPRESS Extension
2//!
3//! This extension defines a new type ...
4//!
5//! * [`CompressionAlgorithm`](crate::extensions::compress::CompressionAlgorithm)
6//!
7//! ... and extends ...
8//!
9//! * the [`Capability`](crate::response::Capability) enum with a new variant [`Capability::Compress`](crate::response::Capability#variant.Compress),
10//! * the [`Command`](crate::command::Command) enum with a new variant [`Command::Compress`](crate::command::Command#variant.Compress), and
11//! * the [`Code`](crate::response::Code) enum with a new variant [`Code::CompressionActive`](crate::response::Code#variant.CompressionActive).
12
13use std::fmt::{Display, Formatter};
14
15#[cfg(feature = "arbitrary")]
16use arbitrary::Arbitrary;
17#[cfg(feature = "bounded-static")]
18use bounded_static::ToStatic;
19#[cfg(feature = "serde")]
20use serde::{Deserialize, Serialize};
21
22use crate::{
23    command::CommandBody,
24    core::Atom,
25    error::{ValidationError, ValidationErrorKind},
26};
27
28impl<'a> CommandBody<'a> {
29    pub fn compress(algorithm: CompressionAlgorithm) -> Self {
30        CommandBody::Compress { algorithm }
31    }
32}
33
34#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
35#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
36#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
37#[derive(Debug, Clone, PartialEq, Eq, Hash)]
38#[non_exhaustive]
39pub enum CompressionAlgorithm {
40    Deflate,
41}
42
43impl Display for CompressionAlgorithm {
44    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45        f.write_str(match self {
46            Self::Deflate => "DEFLATE",
47        })
48    }
49}
50
51impl<'a> TryFrom<&'a str> for CompressionAlgorithm {
52    type Error = ValidationError;
53
54    fn try_from(value: &str) -> Result<Self, Self::Error> {
55        match value.to_ascii_lowercase().as_ref() {
56            "deflate" => Ok(Self::Deflate),
57            _ => Err(ValidationError::new(ValidationErrorKind::Invalid)),
58        }
59    }
60}
61
62impl<'a> TryFrom<&'a [u8]> for CompressionAlgorithm {
63    type Error = ValidationError;
64
65    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
66        match value.to_ascii_lowercase().as_slice() {
67            b"deflate" => Ok(Self::Deflate),
68            _ => Err(ValidationError::new(ValidationErrorKind::Invalid)),
69        }
70    }
71}
72
73impl<'a> TryFrom<Atom<'a>> for CompressionAlgorithm {
74    type Error = ValidationError;
75
76    fn try_from(atom: Atom<'a>) -> Result<Self, Self::Error> {
77        match atom.as_ref().to_ascii_lowercase().as_ref() {
78            "deflate" => Ok(Self::Deflate),
79            _ => Err(ValidationError::new(ValidationErrorKind::Invalid)),
80        }
81    }
82}
83
84impl AsRef<str> for CompressionAlgorithm {
85    fn as_ref(&self) -> &str {
86        match self {
87            CompressionAlgorithm::Deflate => "DEFLATE",
88        }
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn test_conversion() {
98        let tests = [(CompressionAlgorithm::Deflate, "DEFLATE")];
99
100        for (object, string) in tests {
101            // Create from `&[u8]`.
102            let got = CompressionAlgorithm::try_from(string.as_bytes()).unwrap();
103            assert_eq!(object, got);
104
105            // Create from `&str`.
106            let got = CompressionAlgorithm::try_from(string).unwrap();
107            assert_eq!(object, got);
108
109            // Create from `Atom`.
110            let got = CompressionAlgorithm::try_from(Atom::try_from(string).unwrap()).unwrap();
111            assert_eq!(object, got);
112
113            // AsRef
114            let encoded = object.as_ref();
115            assert_eq!(encoded, string);
116        }
117    }
118
119    #[test]
120    fn test_conversion_failing() {
121        let tests = [
122            "", "D", "DE", "DEF", "DEFL", "DEFLA", "DEFLAT", "DEFLATX", "DEFLATEX", "XDEFLATE",
123        ];
124
125        for string in tests {
126            // Create from `&[u8]`.
127            assert!(CompressionAlgorithm::try_from(string.as_bytes()).is_err());
128
129            // Create from `&str`.
130            assert!(CompressionAlgorithm::try_from(string).is_err());
131
132            if !string.is_empty() {
133                // Create from `Atom`.
134                assert!(CompressionAlgorithm::try_from(Atom::try_from(string).unwrap()).is_err());
135            }
136        }
137    }
138}