imap_proto/
types.rs

1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::ops::RangeInclusive;
4
5pub mod acls;
6pub use acls::*;
7
8fn to_owned_cow<T: ?Sized + ToOwned>(c: Cow<'_, T>) -> Cow<'static, T> {
9    Cow::Owned(c.into_owned())
10}
11
12#[derive(Clone, Debug, Eq, PartialEq)]
13pub struct Request<'a>(pub Cow<'a, [u8]>, pub Cow<'a, [u8]>);
14
15#[derive(Copy, Clone, Debug, Eq, PartialEq)]
16pub enum AttrMacro {
17    All,
18    Fast,
19    Full,
20}
21
22#[derive(Debug, Eq, PartialEq)]
23#[non_exhaustive]
24pub enum Response<'a> {
25    Capabilities(Vec<Capability<'a>>),
26    Continue {
27        code: Option<ResponseCode<'a>>,
28        information: Option<Cow<'a, str>>,
29    },
30    Done {
31        tag: RequestId,
32        status: Status,
33        code: Option<ResponseCode<'a>>,
34        information: Option<Cow<'a, str>>,
35    },
36    Data {
37        status: Status,
38        code: Option<ResponseCode<'a>>,
39        information: Option<Cow<'a, str>>,
40    },
41    Expunge(u32),
42    Vanished {
43        earlier: bool,
44        uids: Vec<std::ops::RangeInclusive<u32>>,
45    },
46    Fetch(u32, Vec<AttributeValue<'a>>),
47    MailboxData(MailboxDatum<'a>),
48    Quota(Quota<'a>),
49    QuotaRoot(QuotaRoot<'a>),
50    Id(Option<HashMap<Cow<'a, str>, Cow<'a, str>>>),
51    Acl(Acl<'a>),
52    ListRights(ListRights<'a>),
53    MyRights(MyRights<'a>),
54}
55
56impl<'a> Response<'a> {
57    pub fn from_bytes(buf: &'a [u8]) -> crate::ParseResult<'a> {
58        crate::parser::parse_response(buf)
59    }
60
61    pub fn into_owned(self) -> Response<'static> {
62        match self {
63            Response::Capabilities(capabilities) => Response::Capabilities(
64                capabilities
65                    .into_iter()
66                    .map(Capability::into_owned)
67                    .collect(),
68            ),
69            Response::Continue { code, information } => Response::Continue {
70                code: code.map(ResponseCode::into_owned),
71                information: information.map(to_owned_cow),
72            },
73            Response::Done {
74                tag,
75                status,
76                code,
77                information,
78            } => Response::Done {
79                tag,
80                status,
81                code: code.map(ResponseCode::into_owned),
82                information: information.map(to_owned_cow),
83            },
84            Response::Data {
85                status,
86                code,
87                information,
88            } => Response::Data {
89                status,
90                code: code.map(ResponseCode::into_owned),
91                information: information.map(to_owned_cow),
92            },
93            Response::Expunge(seq) => Response::Expunge(seq),
94            Response::Vanished { earlier, uids } => Response::Vanished { earlier, uids },
95            Response::Fetch(seq, attrs) => Response::Fetch(
96                seq,
97                attrs.into_iter().map(AttributeValue::into_owned).collect(),
98            ),
99            Response::MailboxData(datum) => Response::MailboxData(datum.into_owned()),
100            Response::Quota(quota) => Response::Quota(quota.into_owned()),
101            Response::QuotaRoot(quota_root) => Response::QuotaRoot(quota_root.into_owned()),
102            Response::Id(map) => Response::Id(map.map(|m| {
103                m.into_iter()
104                    .map(|(k, v)| (to_owned_cow(k), to_owned_cow(v)))
105                    .collect()
106            })),
107            Response::Acl(acl_list) => Response::Acl(acl_list.into_owned()),
108            Response::ListRights(rights) => Response::ListRights(rights.into_owned()),
109            Response::MyRights(rights) => Response::MyRights(rights.into_owned()),
110        }
111    }
112}
113
114#[derive(Debug, Eq, PartialEq)]
115pub enum Status {
116    Ok,
117    No,
118    Bad,
119    PreAuth,
120    Bye,
121}
122
123#[derive(Debug, Eq, PartialEq)]
124#[non_exhaustive]
125pub enum ResponseCode<'a> {
126    Alert,
127    BadCharset(Option<Vec<Cow<'a, str>>>),
128    Capabilities(Vec<Capability<'a>>),
129    HighestModSeq(u64), // RFC 4551, section 3.1.1
130    Parse,
131    PermanentFlags(Vec<Cow<'a, str>>),
132    ReadOnly,
133    ReadWrite,
134    TryCreate,
135    UidNext(u32),
136    UidValidity(u32),
137    Unseen(u32),
138    AppendUid(u32, Vec<UidSetMember>),
139    CopyUid(u32, Vec<UidSetMember>, Vec<UidSetMember>),
140    UidNotSticky,
141    MetadataLongEntries(u64), // RFC 5464, section 4.2.1
142    MetadataMaxSize(u64),     // RFC 5464, section 4.3
143    MetadataTooMany,          // RFC 5464, section 4.3
144    MetadataNoPrivate,        // RFC 5464, section 4.3
145}
146
147#[derive(Clone, Debug, Eq, PartialEq)]
148pub enum UidSetMember {
149    UidRange(RangeInclusive<u32>),
150    Uid(u32),
151}
152
153impl From<RangeInclusive<u32>> for UidSetMember {
154    fn from(x: RangeInclusive<u32>) -> Self {
155        UidSetMember::UidRange(x)
156    }
157}
158
159impl From<u32> for UidSetMember {
160    fn from(x: u32) -> Self {
161        UidSetMember::Uid(x)
162    }
163}
164
165impl<'a> ResponseCode<'a> {
166    pub fn into_owned(self) -> ResponseCode<'static> {
167        match self {
168            ResponseCode::Alert => ResponseCode::Alert,
169            ResponseCode::BadCharset(v) => {
170                ResponseCode::BadCharset(v.map(|vs| vs.into_iter().map(to_owned_cow).collect()))
171            }
172            ResponseCode::Capabilities(v) => {
173                ResponseCode::Capabilities(v.into_iter().map(Capability::into_owned).collect())
174            }
175            ResponseCode::HighestModSeq(v) => ResponseCode::HighestModSeq(v),
176            ResponseCode::Parse => ResponseCode::Parse,
177            ResponseCode::PermanentFlags(v) => {
178                ResponseCode::PermanentFlags(v.into_iter().map(to_owned_cow).collect())
179            }
180            ResponseCode::ReadOnly => ResponseCode::ReadOnly,
181            ResponseCode::ReadWrite => ResponseCode::ReadWrite,
182            ResponseCode::TryCreate => ResponseCode::TryCreate,
183            ResponseCode::UidNext(v) => ResponseCode::UidNext(v),
184            ResponseCode::UidValidity(v) => ResponseCode::UidValidity(v),
185            ResponseCode::Unseen(v) => ResponseCode::Unseen(v),
186            ResponseCode::AppendUid(a, b) => ResponseCode::AppendUid(a, b),
187            ResponseCode::CopyUid(a, b, c) => ResponseCode::CopyUid(a, b, c),
188            ResponseCode::UidNotSticky => ResponseCode::UidNotSticky,
189            ResponseCode::MetadataLongEntries(v) => ResponseCode::MetadataLongEntries(v),
190            ResponseCode::MetadataMaxSize(v) => ResponseCode::MetadataMaxSize(v),
191            ResponseCode::MetadataTooMany => ResponseCode::MetadataTooMany,
192            ResponseCode::MetadataNoPrivate => ResponseCode::MetadataNoPrivate,
193        }
194    }
195}
196
197#[derive(Debug, Eq, PartialEq, Clone)]
198#[non_exhaustive]
199pub enum StatusAttribute {
200    HighestModSeq(u64), // RFC 4551
201    Messages(u32),
202    Recent(u32),
203    UidNext(u32),
204    UidValidity(u32),
205    Unseen(u32),
206}
207
208#[derive(Debug, Eq, PartialEq, Clone)]
209pub struct Metadata {
210    pub entry: String,
211    pub value: Option<String>,
212}
213
214#[derive(Debug, Eq, PartialEq, Clone)]
215#[non_exhaustive]
216pub enum MailboxDatum<'a> {
217    Exists(u32),
218    Flags(Vec<Cow<'a, str>>),
219    List {
220        name_attributes: Vec<NameAttribute<'a>>,
221        delimiter: Option<Cow<'a, str>>,
222        name: Cow<'a, str>,
223    },
224    Search(Vec<u32>),
225    Sort(Vec<u32>),
226    Status {
227        mailbox: Cow<'a, str>,
228        status: Vec<StatusAttribute>,
229    },
230    Recent(u32),
231    MetadataSolicited {
232        mailbox: Cow<'a, str>,
233        values: Vec<Metadata>,
234    },
235    MetadataUnsolicited {
236        mailbox: Cow<'a, str>,
237        values: Vec<Cow<'a, str>>,
238    },
239    GmailLabels(Vec<Cow<'a, str>>),
240    GmailMsgId(u64),
241}
242
243impl<'a> MailboxDatum<'a> {
244    pub fn into_owned(self) -> MailboxDatum<'static> {
245        match self {
246            MailboxDatum::Exists(seq) => MailboxDatum::Exists(seq),
247            MailboxDatum::Flags(flags) => {
248                MailboxDatum::Flags(flags.into_iter().map(to_owned_cow).collect())
249            }
250            MailboxDatum::List {
251                name_attributes,
252                delimiter,
253                name,
254            } => MailboxDatum::List {
255                name_attributes: name_attributes
256                    .into_iter()
257                    .map(|named_attribute| named_attribute.into_owned())
258                    .collect(),
259                delimiter: delimiter.map(to_owned_cow),
260                name: to_owned_cow(name),
261            },
262            MailboxDatum::Search(seqs) => MailboxDatum::Search(seqs),
263            MailboxDatum::Sort(seqs) => MailboxDatum::Sort(seqs),
264            MailboxDatum::Status { mailbox, status } => MailboxDatum::Status {
265                mailbox: to_owned_cow(mailbox),
266                status,
267            },
268            MailboxDatum::Recent(seq) => MailboxDatum::Recent(seq),
269            MailboxDatum::MetadataSolicited { mailbox, values } => {
270                MailboxDatum::MetadataSolicited {
271                    mailbox: to_owned_cow(mailbox),
272                    values,
273                }
274            }
275            MailboxDatum::MetadataUnsolicited { mailbox, values } => {
276                MailboxDatum::MetadataUnsolicited {
277                    mailbox: to_owned_cow(mailbox),
278                    values: values.into_iter().map(to_owned_cow).collect(),
279                }
280            }
281            MailboxDatum::GmailLabels(labels) => {
282                MailboxDatum::GmailLabels(labels.into_iter().map(to_owned_cow).collect())
283            }
284            MailboxDatum::GmailMsgId(msgid) => MailboxDatum::GmailMsgId(msgid),
285        }
286    }
287}
288
289#[derive(Debug, Eq, PartialEq, Hash)]
290pub enum Capability<'a> {
291    Imap4rev1,
292    Auth(Cow<'a, str>),
293    Atom(Cow<'a, str>),
294}
295
296impl<'a> Capability<'a> {
297    pub fn into_owned(self) -> Capability<'static> {
298        match self {
299            Capability::Imap4rev1 => Capability::Imap4rev1,
300            Capability::Auth(v) => Capability::Auth(to_owned_cow(v)),
301            Capability::Atom(v) => Capability::Atom(to_owned_cow(v)),
302        }
303    }
304}
305
306#[derive(Debug, Eq, PartialEq)]
307#[non_exhaustive]
308pub enum Attribute {
309    Body,
310    Envelope,
311    Flags,
312    InternalDate,
313    ModSeq, // RFC 4551, section 3.3.2
314    Rfc822,
315    Rfc822Size,
316    Rfc822Text,
317    Uid,
318    /// https://developers.google.com/gmail/imap/imap-extensions#access_to_gmail_labels_x-gm-labels
319    GmailLabels,
320    GmailMsgId,
321}
322
323#[derive(Debug, Eq, PartialEq)]
324pub enum MessageSection {
325    Header,
326    Mime,
327    Text,
328}
329
330#[derive(Debug, Eq, PartialEq)]
331pub enum SectionPath {
332    Full(MessageSection),
333    Part(Vec<u32>, Option<MessageSection>),
334}
335
336#[allow(clippy::large_enum_variant)]
337#[derive(Debug, Eq, PartialEq)]
338#[non_exhaustive]
339pub enum AttributeValue<'a> {
340    BodySection {
341        section: Option<SectionPath>,
342        index: Option<u32>,
343        data: Option<Cow<'a, [u8]>>,
344    },
345    BodyStructure(BodyStructure<'a>),
346    Envelope(Box<Envelope<'a>>),
347    Flags(Vec<Cow<'a, str>>),
348    InternalDate(Cow<'a, str>),
349    ModSeq(u64), // RFC 4551, section 3.3.2
350    Rfc822(Option<Cow<'a, [u8]>>),
351    Rfc822Header(Option<Cow<'a, [u8]>>),
352    Rfc822Size(u32),
353    Rfc822Text(Option<Cow<'a, [u8]>>),
354    Uid(u32),
355    /// https://developers.google.com/gmail/imap/imap-extensions#access_to_gmail_labels_x-gm-labels
356    GmailLabels(Vec<Cow<'a, str>>),
357    GmailMsgId(u64),
358}
359
360impl<'a> AttributeValue<'a> {
361    pub fn into_owned(self) -> AttributeValue<'static> {
362        match self {
363            AttributeValue::BodySection {
364                section,
365                index,
366                data,
367            } => AttributeValue::BodySection {
368                section,
369                index,
370                data: data.map(to_owned_cow),
371            },
372            AttributeValue::BodyStructure(body) => AttributeValue::BodyStructure(body.into_owned()),
373            AttributeValue::Envelope(e) => AttributeValue::Envelope(Box::new(e.into_owned())),
374            AttributeValue::Flags(v) => {
375                AttributeValue::Flags(v.into_iter().map(to_owned_cow).collect())
376            }
377            AttributeValue::InternalDate(v) => AttributeValue::InternalDate(to_owned_cow(v)),
378            AttributeValue::ModSeq(v) => AttributeValue::ModSeq(v),
379            AttributeValue::Rfc822(v) => AttributeValue::Rfc822(v.map(to_owned_cow)),
380            AttributeValue::Rfc822Header(v) => AttributeValue::Rfc822Header(v.map(to_owned_cow)),
381            AttributeValue::Rfc822Size(v) => AttributeValue::Rfc822Size(v),
382            AttributeValue::Rfc822Text(v) => AttributeValue::Rfc822Text(v.map(to_owned_cow)),
383            AttributeValue::Uid(v) => AttributeValue::Uid(v),
384            AttributeValue::GmailLabels(v) => {
385                AttributeValue::GmailLabels(v.into_iter().map(to_owned_cow).collect())
386            }
387            AttributeValue::GmailMsgId(v) => AttributeValue::GmailMsgId(v),
388        }
389    }
390}
391
392#[allow(clippy::large_enum_variant)]
393#[derive(Debug, Eq, PartialEq)]
394pub enum BodyStructure<'a> {
395    Basic {
396        common: BodyContentCommon<'a>,
397        other: BodyContentSinglePart<'a>,
398        extension: Option<BodyExtension<'a>>,
399    },
400    Text {
401        common: BodyContentCommon<'a>,
402        other: BodyContentSinglePart<'a>,
403        lines: u32,
404        extension: Option<BodyExtension<'a>>,
405    },
406    Message {
407        common: BodyContentCommon<'a>,
408        other: BodyContentSinglePart<'a>,
409        envelope: Envelope<'a>,
410        body: Box<BodyStructure<'a>>,
411        lines: u32,
412        extension: Option<BodyExtension<'a>>,
413    },
414    Multipart {
415        common: BodyContentCommon<'a>,
416        bodies: Vec<BodyStructure<'a>>,
417        extension: Option<BodyExtension<'a>>,
418    },
419}
420
421impl<'a> BodyStructure<'a> {
422    pub fn into_owned(self) -> BodyStructure<'static> {
423        match self {
424            BodyStructure::Basic {
425                common,
426                other,
427                extension,
428            } => BodyStructure::Basic {
429                common: common.into_owned(),
430                other: other.into_owned(),
431                extension: extension.map(|v| v.into_owned()),
432            },
433            BodyStructure::Text {
434                common,
435                other,
436                lines,
437                extension,
438            } => BodyStructure::Text {
439                common: common.into_owned(),
440                other: other.into_owned(),
441                lines,
442                extension: extension.map(|v| v.into_owned()),
443            },
444            BodyStructure::Message {
445                common,
446                other,
447                envelope,
448                body,
449                lines,
450                extension,
451            } => BodyStructure::Message {
452                common: common.into_owned(),
453                other: other.into_owned(),
454                envelope: envelope.into_owned(),
455                body: Box::new(body.into_owned()),
456                lines,
457                extension: extension.map(|v| v.into_owned()),
458            },
459            BodyStructure::Multipart {
460                common,
461                bodies,
462                extension,
463            } => BodyStructure::Multipart {
464                common: common.into_owned(),
465                bodies: bodies.into_iter().map(|v| v.into_owned()).collect(),
466                extension: extension.map(|v| v.into_owned()),
467            },
468        }
469    }
470}
471
472#[derive(Debug, Eq, PartialEq)]
473pub struct BodyContentCommon<'a> {
474    pub ty: ContentType<'a>,
475    pub disposition: Option<ContentDisposition<'a>>,
476    pub language: Option<Vec<Cow<'a, str>>>,
477    pub location: Option<Cow<'a, str>>,
478}
479
480impl<'a> BodyContentCommon<'a> {
481    pub fn into_owned(self) -> BodyContentCommon<'static> {
482        BodyContentCommon {
483            ty: self.ty.into_owned(),
484            disposition: self.disposition.map(|v| v.into_owned()),
485            language: self
486                .language
487                .map(|v| v.into_iter().map(to_owned_cow).collect()),
488            location: self.location.map(to_owned_cow),
489        }
490    }
491}
492
493#[derive(Debug, Eq, PartialEq)]
494pub struct BodyContentSinglePart<'a> {
495    pub id: Option<Cow<'a, str>>,
496    pub md5: Option<Cow<'a, str>>,
497    pub description: Option<Cow<'a, str>>,
498    pub transfer_encoding: ContentEncoding<'a>,
499    pub octets: u32,
500}
501
502impl<'a> BodyContentSinglePart<'a> {
503    pub fn into_owned(self) -> BodyContentSinglePart<'static> {
504        BodyContentSinglePart {
505            id: self.id.map(to_owned_cow),
506            md5: self.md5.map(to_owned_cow),
507            description: self.description.map(to_owned_cow),
508            transfer_encoding: self.transfer_encoding.into_owned(),
509            octets: self.octets,
510        }
511    }
512}
513
514#[derive(Debug, Eq, PartialEq)]
515pub struct ContentType<'a> {
516    pub ty: Cow<'a, str>,
517    pub subtype: Cow<'a, str>,
518    pub params: BodyParams<'a>,
519}
520
521impl<'a> ContentType<'a> {
522    pub fn into_owned(self) -> ContentType<'static> {
523        ContentType {
524            ty: to_owned_cow(self.ty),
525            subtype: to_owned_cow(self.subtype),
526            params: body_param_owned(self.params),
527        }
528    }
529}
530
531#[derive(Debug, Eq, PartialEq)]
532pub struct ContentDisposition<'a> {
533    pub ty: Cow<'a, str>,
534    pub params: BodyParams<'a>,
535}
536
537impl<'a> ContentDisposition<'a> {
538    pub fn into_owned(self) -> ContentDisposition<'static> {
539        ContentDisposition {
540            ty: to_owned_cow(self.ty),
541            params: body_param_owned(self.params),
542        }
543    }
544}
545
546#[derive(Debug, Eq, PartialEq)]
547pub enum ContentEncoding<'a> {
548    SevenBit,
549    EightBit,
550    Binary,
551    Base64,
552    QuotedPrintable,
553    Other(Cow<'a, str>),
554}
555
556impl<'a> ContentEncoding<'a> {
557    pub fn into_owned(self) -> ContentEncoding<'static> {
558        match self {
559            ContentEncoding::SevenBit => ContentEncoding::SevenBit,
560            ContentEncoding::EightBit => ContentEncoding::EightBit,
561            ContentEncoding::Binary => ContentEncoding::Binary,
562            ContentEncoding::Base64 => ContentEncoding::Base64,
563            ContentEncoding::QuotedPrintable => ContentEncoding::QuotedPrintable,
564            ContentEncoding::Other(v) => ContentEncoding::Other(to_owned_cow(v)),
565        }
566    }
567}
568
569#[derive(Debug, Eq, PartialEq)]
570pub enum BodyExtension<'a> {
571    Num(u32),
572    Str(Option<Cow<'a, str>>),
573    List(Vec<BodyExtension<'a>>),
574}
575
576impl<'a> BodyExtension<'a> {
577    pub fn into_owned(self) -> BodyExtension<'static> {
578        match self {
579            BodyExtension::Num(v) => BodyExtension::Num(v),
580            BodyExtension::Str(v) => BodyExtension::Str(v.map(to_owned_cow)),
581            BodyExtension::List(v) => {
582                BodyExtension::List(v.into_iter().map(|v| v.into_owned()).collect())
583            }
584        }
585    }
586}
587
588pub type BodyParams<'a> = Option<Vec<(Cow<'a, str>, Cow<'a, str>)>>;
589
590fn body_param_owned(v: BodyParams<'_>) -> BodyParams<'static> {
591    v.map(|v| {
592        v.into_iter()
593            .map(|(k, v)| (to_owned_cow(k), to_owned_cow(v)))
594            .collect()
595    })
596}
597
598/// An RFC 2822 envelope
599///
600/// See https://datatracker.ietf.org/doc/html/rfc2822#section-3.6 for more details.
601#[derive(Debug, Eq, PartialEq)]
602pub struct Envelope<'a> {
603    pub date: Option<Cow<'a, [u8]>>,
604    pub subject: Option<Cow<'a, [u8]>>,
605    /// Author of the message; mailbox responsible for writing the message
606    pub from: Option<Vec<Address<'a>>>,
607    /// Mailbox of the agent responsible for the message's transmission
608    pub sender: Option<Vec<Address<'a>>>,
609    /// Mailbox that the author of the message suggests replies be sent to
610    pub reply_to: Option<Vec<Address<'a>>>,
611    pub to: Option<Vec<Address<'a>>>,
612    pub cc: Option<Vec<Address<'a>>>,
613    pub bcc: Option<Vec<Address<'a>>>,
614    pub in_reply_to: Option<Cow<'a, [u8]>>,
615    pub message_id: Option<Cow<'a, [u8]>>,
616}
617
618impl<'a> Envelope<'a> {
619    pub fn into_owned(self) -> Envelope<'static> {
620        Envelope {
621            date: self.date.map(to_owned_cow),
622            subject: self.subject.map(to_owned_cow),
623            from: self
624                .from
625                .map(|v| v.into_iter().map(|v| v.into_owned()).collect()),
626            sender: self
627                .sender
628                .map(|v| v.into_iter().map(|v| v.into_owned()).collect()),
629            reply_to: self
630                .reply_to
631                .map(|v| v.into_iter().map(|v| v.into_owned()).collect()),
632            to: self
633                .to
634                .map(|v| v.into_iter().map(|v| v.into_owned()).collect()),
635            cc: self
636                .cc
637                .map(|v| v.into_iter().map(|v| v.into_owned()).collect()),
638            bcc: self
639                .bcc
640                .map(|v| v.into_iter().map(|v| v.into_owned()).collect()),
641            in_reply_to: self.in_reply_to.map(to_owned_cow),
642            message_id: self.message_id.map(to_owned_cow),
643        }
644    }
645}
646
647#[derive(Debug, Eq, PartialEq)]
648pub struct Address<'a> {
649    pub name: Option<Cow<'a, [u8]>>,
650    pub adl: Option<Cow<'a, [u8]>>,
651    pub mailbox: Option<Cow<'a, [u8]>>,
652    pub host: Option<Cow<'a, [u8]>>,
653}
654
655impl<'a> Address<'a> {
656    pub fn into_owned(self) -> Address<'static> {
657        Address {
658            name: self.name.map(to_owned_cow),
659            adl: self.adl.map(to_owned_cow),
660            mailbox: self.mailbox.map(to_owned_cow),
661            host: self.host.map(to_owned_cow),
662        }
663    }
664}
665
666#[derive(Clone, Debug, Eq, PartialEq)]
667pub struct RequestId(pub String);
668
669impl RequestId {
670    pub fn as_bytes(&self) -> &[u8] {
671        self.0.as_bytes()
672    }
673}
674
675#[derive(Clone, Copy, Debug, Eq, PartialEq)]
676pub enum State {
677    NotAuthenticated,
678    Authenticated,
679    Selected,
680    Logout,
681}
682
683// Body Structure
684
685pub struct BodyFields<'a> {
686    pub param: BodyParams<'a>,
687    pub id: Option<Cow<'a, str>>,
688    pub description: Option<Cow<'a, str>>,
689    pub transfer_encoding: ContentEncoding<'a>,
690    pub octets: u32,
691}
692
693impl<'a> BodyFields<'a> {
694    pub fn into_owned(self) -> BodyFields<'static> {
695        BodyFields {
696            param: body_param_owned(self.param),
697            id: self.id.map(to_owned_cow),
698            description: self.description.map(to_owned_cow),
699            transfer_encoding: self.transfer_encoding.into_owned(),
700            octets: self.octets,
701        }
702    }
703}
704
705pub struct BodyExt1Part<'a> {
706    pub md5: Option<Cow<'a, str>>,
707    pub disposition: Option<ContentDisposition<'a>>,
708    pub language: Option<Vec<Cow<'a, str>>>,
709    pub location: Option<Cow<'a, str>>,
710    pub extension: Option<BodyExtension<'a>>,
711}
712
713impl<'a> BodyExt1Part<'a> {
714    pub fn into_owned(self) -> BodyExt1Part<'static> {
715        BodyExt1Part {
716            md5: self.md5.map(to_owned_cow),
717            disposition: self.disposition.map(|v| v.into_owned()),
718            language: self
719                .language
720                .map(|v| v.into_iter().map(to_owned_cow).collect()),
721            location: self.location.map(to_owned_cow),
722            extension: self.extension.map(|v| v.into_owned()),
723        }
724    }
725}
726
727pub struct BodyExtMPart<'a> {
728    pub param: BodyParams<'a>,
729    pub disposition: Option<ContentDisposition<'a>>,
730    pub language: Option<Vec<Cow<'a, str>>>,
731    pub location: Option<Cow<'a, str>>,
732    pub extension: Option<BodyExtension<'a>>,
733}
734
735impl<'a> BodyExtMPart<'a> {
736    pub fn into_owned(self) -> BodyExtMPart<'static> {
737        BodyExtMPart {
738            param: body_param_owned(self.param),
739            disposition: self.disposition.map(|v| v.into_owned()),
740            language: self
741                .language
742                .map(|v| v.into_iter().map(to_owned_cow).collect()),
743            location: self.location.map(to_owned_cow),
744            extension: self.extension.map(|v| v.into_owned()),
745        }
746    }
747}
748
749/// The name attributes are returned as part of a LIST response described in
750/// [RFC 3501 section 7.2.2](https://tools.ietf.org/html/rfc3501#section-7.2.2).
751///
752/// This enumeration additional includes values from the extension Special-Use
753/// Mailboxes [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2).
754#[derive(Debug, Eq, PartialEq, Clone)]
755#[non_exhaustive]
756pub enum NameAttribute<'a> {
757    /// From [RFC 3501 section 7.2.2](https://tools.ietf.org/html/rfc3501#section-7.2.2):
758    ///
759    /// > It is not possible for any child levels of hierarchy to exist
760    /// > under this name; no child levels exist now and none can be
761    /// > created in the future.
762    NoInferiors,
763    /// From [RFC 3501 section 7.2.2](https://tools.ietf.org/html/rfc3501#section-7.2.2):
764    ///
765    /// > It is not possible to use this name as a selectable mailbox.
766    NoSelect,
767    /// From [RFC 3501 section 7.2.2](https://tools.ietf.org/html/rfc3501#section-7.2.2):
768    ///
769    /// > The mailbox has been marked "interesting" by the server; the
770    /// > mailbox probably contains messages that have been added since
771    /// > the last time the mailbox was selected.
772    Marked,
773    /// From [RFC 3501 section 7.2.2](https://tools.ietf.org/html/rfc3501#section-7.2.2):
774    ///
775    /// > The mailbox does not contain any additional messages since the
776    /// > last time the mailbox was selected.
777    Unmarked,
778    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2):
779    ///
780    /// > This mailbox presents all messages in the user's message store.
781    /// > Implementations MAY omit some messages, such as, perhaps, those
782    /// > in \Trash and \Junk.  When this special use is supported, it is
783    /// > almost certain to represent a virtual mailbox.
784    All,
785    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2):
786    ///
787    /// > This mailbox is used to archive messages.  The meaning of an
788    /// > "archival" mailbox is server-dependent; typically, it will be
789    /// > used to get messages out of the inbox, or otherwise keep them
790    /// > out of the user's way, while still making them accessible.
791    Archive,
792    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2):
793    ///
794    /// > This mailbox is used to hold draft messages -- typically,
795    /// > messages that are being composed but have not yet been sent.  In
796    /// > some server implementations, this might be a virtual mailbox,
797    /// > containing messages from other mailboxes that are marked with
798    /// > the "\Draft" message flag.  Alternatively, this might just be
799    /// > advice that a client put drafts here.
800    Drafts,
801    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2):
802    ///
803    /// > This mailbox presents all messages marked in some way as
804    /// > "important".  When this special use is supported, it is likely
805    /// > to represent a virtual mailbox collecting messages (from other
806    /// > mailboxes) that are marked with the "\Flagged" message flag.
807    Flagged,
808    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2):
809    ///
810    /// > This mailbox is where messages deemed to be junk mail are held.
811    /// > Some server implementations might put messages here
812    /// > automatically.  Alternatively, this might just be advice to a
813    /// > client-side spam filter.
814    Junk,
815    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2):
816    ///
817    /// > This mailbox is used to hold copies of messages that have been
818    /// > sent.  Some server implementations might put messages here
819    /// > automatically.  Alternatively, this might just be advice that a
820    /// > client save sent messages here.
821    Sent,
822    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2)
823    ///
824    /// > This mailbox is used to hold messages that have been deleted or
825    /// > marked for deletion.  In some server implementations, this might
826    /// > be a virtual mailbox, containing messages from other mailboxes
827    /// > that are marked with the "\Deleted" message flag.
828    /// > Alternatively, this might just be advice that a client that
829    /// > chooses not to use the IMAP "\Deleted" model should use this as
830    /// > its trash location.  In server implementations that strictly
831    /// > expect the IMAP "\Deleted" model, this special use is likely not
832    /// > to be supported.
833    Trash,
834    /// A name attribute not defined in [RFC 3501 section 7.2.2](https://tools.ietf.org/html/rfc3501#section-7.2.2)
835    /// or any supported extension.
836    Extension(Cow<'a, str>),
837}
838
839impl<'a> NameAttribute<'a> {
840    pub fn into_owned(self) -> NameAttribute<'static> {
841        match self {
842            // RFC 3501
843            NameAttribute::NoInferiors => NameAttribute::NoInferiors,
844            NameAttribute::NoSelect => NameAttribute::NoSelect,
845            NameAttribute::Marked => NameAttribute::Marked,
846            NameAttribute::Unmarked => NameAttribute::Unmarked,
847            // RFC 6154
848            NameAttribute::All => NameAttribute::All,
849            NameAttribute::Archive => NameAttribute::Archive,
850            NameAttribute::Drafts => NameAttribute::Drafts,
851            NameAttribute::Flagged => NameAttribute::Flagged,
852            NameAttribute::Junk => NameAttribute::Junk,
853            NameAttribute::Sent => NameAttribute::Sent,
854            NameAttribute::Trash => NameAttribute::Trash,
855            // Extensions not supported by this crate
856            NameAttribute::Extension(s) => NameAttribute::Extension(to_owned_cow(s)),
857        }
858    }
859}
860
861// IMAP4 QUOTA extension (rfc2087)
862
863/// https://tools.ietf.org/html/rfc2087#section-3
864#[derive(Debug, Eq, PartialEq, Hash, Clone)]
865pub enum QuotaResourceName<'a> {
866    /// Sum of messages' RFC822.SIZE, in units of 1024 octets
867    Storage,
868    /// Number of messages
869    Message,
870    Atom(Cow<'a, str>),
871}
872
873impl<'a> QuotaResourceName<'a> {
874    pub fn into_owned(self) -> QuotaResourceName<'static> {
875        match self {
876            QuotaResourceName::Message => QuotaResourceName::Message,
877            QuotaResourceName::Storage => QuotaResourceName::Storage,
878            QuotaResourceName::Atom(v) => QuotaResourceName::Atom(to_owned_cow(v)),
879        }
880    }
881}
882
883/// 5.1. QUOTA Response (https://tools.ietf.org/html/rfc2087#section-5.1)
884#[derive(Debug, Eq, PartialEq, Hash, Clone)]
885pub struct QuotaResource<'a> {
886    pub name: QuotaResourceName<'a>,
887    /// current usage of the resource
888    pub usage: u64,
889    /// resource limit
890    pub limit: u64,
891}
892
893impl<'a> QuotaResource<'a> {
894    pub fn into_owned(self) -> QuotaResource<'static> {
895        QuotaResource {
896            name: self.name.into_owned(),
897            usage: self.usage,
898            limit: self.limit,
899        }
900    }
901}
902
903/// 5.1. QUOTA Response (https://tools.ietf.org/html/rfc2087#section-5.1)
904#[derive(Debug, Eq, PartialEq, Hash, Clone)]
905pub struct Quota<'a> {
906    /// quota root name
907    pub root_name: Cow<'a, str>,
908    pub resources: Vec<QuotaResource<'a>>,
909}
910
911impl<'a> Quota<'a> {
912    pub fn into_owned(self) -> Quota<'static> {
913        Quota {
914            root_name: to_owned_cow(self.root_name),
915            resources: self.resources.into_iter().map(|r| r.into_owned()).collect(),
916        }
917    }
918}
919
920/// 5.2. QUOTAROOT Response (https://tools.ietf.org/html/rfc2087#section-5.2)
921#[derive(Debug, Eq, PartialEq, Hash, Clone)]
922pub struct QuotaRoot<'a> {
923    /// mailbox name
924    pub mailbox_name: Cow<'a, str>,
925    /// zero or more quota root names
926    pub quota_root_names: Vec<Cow<'a, str>>,
927}
928
929impl<'a> QuotaRoot<'a> {
930    pub fn into_owned(self) -> QuotaRoot<'static> {
931        QuotaRoot {
932            mailbox_name: to_owned_cow(self.mailbox_name),
933            quota_root_names: self
934                .quota_root_names
935                .into_iter()
936                .map(to_owned_cow)
937                .collect(),
938        }
939    }
940}
941
942#[cfg(test)]
943mod tests {
944    use super::*;
945
946    /// Tests that the [`NameAttribute::into_owned`] method returns the
947    /// same value (the ownership should only change).
948    #[test]
949    fn test_name_attribute_into_owned() {
950        let name_attributes = [
951            // RFC 3501
952            NameAttribute::NoInferiors,
953            NameAttribute::NoSelect,
954            NameAttribute::Marked,
955            NameAttribute::Unmarked,
956            // RFC 6154
957            NameAttribute::All,
958            NameAttribute::Archive,
959            NameAttribute::Drafts,
960            NameAttribute::Flagged,
961            NameAttribute::Junk,
962            NameAttribute::Sent,
963            NameAttribute::Trash,
964            // Extensions not supported by this crate
965            NameAttribute::Extension(Cow::Borrowed("Foobar")),
966        ];
967
968        for name_attribute in name_attributes {
969            let owned_name_attribute = name_attribute.clone().into_owned();
970            assert_eq!(name_attribute, owned_name_attribute);
971        }
972    }
973}