imap_types/extensions/
enable.rs

1//! The IMAP ENABLE Extension
2//!
3//! This extension extends ...
4//!
5//! * the [Capability](crate::response::Capability) enum with a new variant [Capability::Enable](crate::response::Capability#variant.Enable),
6//! * the [CommandBody](crate::command::CommandBody) enum with a new variant [CommandBody::Enable](crate::command::CommandBody#variant.Enable), and
7//! * the [Data](crate::response::Data) enum with a new variant [Data::Enabled](crate::response::Data#variant.Enabled).
8
9use std::fmt::{Display, Formatter};
10
11#[cfg(feature = "arbitrary")]
12use arbitrary::Arbitrary;
13#[cfg(feature = "bounded-static")]
14use bounded_static::ToStatic;
15#[cfg(feature = "serde")]
16use serde::{Deserialize, Serialize};
17
18use crate::{
19    command::CommandBody,
20    core::{Atom, NonEmptyVec},
21    error::ValidationError,
22};
23
24impl<'a> CommandBody<'a> {
25    pub fn enable<C>(capabilities: C) -> Result<Self, C::Error>
26    where
27        C: TryInto<NonEmptyVec<CapabilityEnable<'a>>>,
28    {
29        Ok(CommandBody::Enable {
30            capabilities: capabilities.try_into()?,
31        })
32    }
33}
34
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 CapabilityEnable<'a> {
40    Utf8(Utf8Kind),
41    #[cfg(feature = "ext_condstore_qresync")]
42    #[cfg_attr(docsrs, doc(cfg(feature = "ext_condstore_qresync")))]
43    CondStore,
44    Other(CapabilityEnableOther<'a>),
45}
46
47impl<'a> TryFrom<&'a str> for CapabilityEnable<'a> {
48    type Error = ValidationError;
49
50    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
51        Ok(Self::from(Atom::try_from(value)?))
52    }
53}
54
55impl<'a> From<Atom<'a>> for CapabilityEnable<'a> {
56    fn from(atom: Atom<'a>) -> Self {
57        match atom.as_ref().to_ascii_lowercase().as_ref() {
58            "utf8=accept" => Self::Utf8(Utf8Kind::Accept),
59            "utf8=only" => Self::Utf8(Utf8Kind::Only),
60            #[cfg(feature = "ext_condstore_qresync")]
61            "condstore" => Self::CondStore,
62            _ => Self::Other(CapabilityEnableOther(atom)),
63        }
64    }
65}
66
67impl<'a> Display for CapabilityEnable<'a> {
68    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
69        match self {
70            Self::Utf8(kind) => write!(f, "UTF8={}", kind),
71            #[cfg(feature = "ext_condstore_qresync")]
72            Self::CondStore => write!(f, "CONDSTORE"),
73            Self::Other(other) => write!(f, "{}", other.0),
74        }
75    }
76}
77
78/// An (unknown) capability.
79///
80/// It's guaranteed that this type can't represent any capability from [`CapabilityEnable`].
81#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
82#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
83#[derive(Debug, Clone, PartialEq, Eq, Hash)]
84pub struct CapabilityEnableOther<'a>(Atom<'a>);
85
86#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
87#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
88#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
89#[derive(Debug, Clone, PartialEq, Eq, Hash)]
90#[non_exhaustive]
91pub enum Utf8Kind {
92    Accept,
93    Only,
94}
95
96impl Display for Utf8Kind {
97    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
98        f.write_str(match self {
99            Self::Accept => "ACCEPT",
100            Self::Only => "ONLY",
101        })
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_conversion_capability_enable() {
111        assert_eq!(
112            CapabilityEnable::from(Atom::try_from("utf8=only").unwrap()),
113            CapabilityEnable::Utf8(Utf8Kind::Only)
114        );
115        assert_eq!(
116            CapabilityEnable::from(Atom::try_from("utf8=accept").unwrap()),
117            CapabilityEnable::Utf8(Utf8Kind::Accept)
118        );
119        assert_eq!(
120            CapabilityEnable::try_from("utf").unwrap(),
121            CapabilityEnable::Other(CapabilityEnableOther(Atom::try_from("utf").unwrap()))
122        );
123        assert_eq!(
124            CapabilityEnable::try_from("xxxxx").unwrap(),
125            CapabilityEnable::Other(CapabilityEnableOther(Atom::try_from("xxxxx").unwrap()))
126        );
127    }
128}