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    GmailThrId(u64),
242}
243
244impl<'a> MailboxDatum<'a> {
245    pub fn into_owned(self) -> MailboxDatum<'static> {
246        match self {
247            MailboxDatum::Exists(seq) => MailboxDatum::Exists(seq),
248            MailboxDatum::Flags(flags) => {
249                MailboxDatum::Flags(flags.into_iter().map(to_owned_cow).collect())
250            }
251            MailboxDatum::List {
252                name_attributes,
253                delimiter,
254                name,
255            } => MailboxDatum::List {
256                name_attributes: name_attributes
257                    .into_iter()
258                    .map(|named_attribute| named_attribute.into_owned())
259                    .collect(),
260                delimiter: delimiter.map(to_owned_cow),
261                name: to_owned_cow(name),
262            },
263            MailboxDatum::Search(seqs) => MailboxDatum::Search(seqs),
264            MailboxDatum::Sort(seqs) => MailboxDatum::Sort(seqs),
265            MailboxDatum::Status { mailbox, status } => MailboxDatum::Status {
266                mailbox: to_owned_cow(mailbox),
267                status,
268            },
269            MailboxDatum::Recent(seq) => MailboxDatum::Recent(seq),
270            MailboxDatum::MetadataSolicited { mailbox, values } => {
271                MailboxDatum::MetadataSolicited {
272                    mailbox: to_owned_cow(mailbox),
273                    values,
274                }
275            }
276            MailboxDatum::MetadataUnsolicited { mailbox, values } => {
277                MailboxDatum::MetadataUnsolicited {
278                    mailbox: to_owned_cow(mailbox),
279                    values: values.into_iter().map(to_owned_cow).collect(),
280                }
281            }
282            MailboxDatum::GmailLabels(labels) => {
283                MailboxDatum::GmailLabels(labels.into_iter().map(to_owned_cow).collect())
284            }
285            MailboxDatum::GmailMsgId(msgid) => MailboxDatum::GmailMsgId(msgid),
286            MailboxDatum::GmailThrId(thrid) => MailboxDatum::GmailThrId(thrid),
287        }
288    }
289}
290
291#[derive(Debug, Eq, PartialEq, Hash)]
292pub enum Capability<'a> {
293    Imap4rev1,
294    Auth(Cow<'a, str>),
295    Atom(Cow<'a, str>),
296}
297
298impl<'a> Capability<'a> {
299    pub fn into_owned(self) -> Capability<'static> {
300        match self {
301            Capability::Imap4rev1 => Capability::Imap4rev1,
302            Capability::Auth(v) => Capability::Auth(to_owned_cow(v)),
303            Capability::Atom(v) => Capability::Atom(to_owned_cow(v)),
304        }
305    }
306}
307
308#[derive(Debug, Eq, PartialEq)]
309#[non_exhaustive]
310pub enum Attribute {
311    Body,
312    Envelope,
313    Flags,
314    InternalDate,
315    ModSeq, // RFC 4551, section 3.3.2
316    Rfc822,
317    Rfc822Size,
318    Rfc822Text,
319    Uid,
320    /// https://developers.google.com/gmail/imap/imap-extensions#access_to_gmail_labels_x-gm-labels
321    GmailLabels,
322    GmailMsgId,
323    GmailThrId,
324}
325
326#[derive(Debug, Eq, PartialEq)]
327pub enum MessageSection {
328    Header,
329    Mime,
330    Text,
331}
332
333#[derive(Debug, Eq, PartialEq)]
334pub enum SectionPath {
335    Full(MessageSection),
336    Part(Vec<u32>, Option<MessageSection>),
337}
338
339#[allow(clippy::large_enum_variant)]
340#[derive(Debug, Eq, PartialEq)]
341#[non_exhaustive]
342pub enum AttributeValue<'a> {
343    BodySection {
344        section: Option<SectionPath>,
345        index: Option<u32>,
346        data: Option<Cow<'a, [u8]>>,
347    },
348    BodyStructure(BodyStructure<'a>),
349    Envelope(Box<Envelope<'a>>),
350    Flags(Vec<Cow<'a, str>>),
351    InternalDate(Cow<'a, str>),
352    ModSeq(u64), // RFC 4551, section 3.3.2
353    Rfc822(Option<Cow<'a, [u8]>>),
354    Rfc822Header(Option<Cow<'a, [u8]>>),
355    Rfc822Size(u32),
356    Rfc822Text(Option<Cow<'a, [u8]>>),
357    Uid(u32),
358    /// https://developers.google.com/gmail/imap/imap-extensions#access_to_gmail_labels_x-gm-labels
359    GmailLabels(Vec<Cow<'a, str>>),
360    GmailMsgId(u64),
361    GmailThrId(u64),
362}
363
364impl<'a> AttributeValue<'a> {
365    pub fn into_owned(self) -> AttributeValue<'static> {
366        match self {
367            AttributeValue::BodySection {
368                section,
369                index,
370                data,
371            } => AttributeValue::BodySection {
372                section,
373                index,
374                data: data.map(to_owned_cow),
375            },
376            AttributeValue::BodyStructure(body) => AttributeValue::BodyStructure(body.into_owned()),
377            AttributeValue::Envelope(e) => AttributeValue::Envelope(Box::new(e.into_owned())),
378            AttributeValue::Flags(v) => {
379                AttributeValue::Flags(v.into_iter().map(to_owned_cow).collect())
380            }
381            AttributeValue::InternalDate(v) => AttributeValue::InternalDate(to_owned_cow(v)),
382            AttributeValue::ModSeq(v) => AttributeValue::ModSeq(v),
383            AttributeValue::Rfc822(v) => AttributeValue::Rfc822(v.map(to_owned_cow)),
384            AttributeValue::Rfc822Header(v) => AttributeValue::Rfc822Header(v.map(to_owned_cow)),
385            AttributeValue::Rfc822Size(v) => AttributeValue::Rfc822Size(v),
386            AttributeValue::Rfc822Text(v) => AttributeValue::Rfc822Text(v.map(to_owned_cow)),
387            AttributeValue::Uid(v) => AttributeValue::Uid(v),
388            AttributeValue::GmailLabels(v) => {
389                AttributeValue::GmailLabels(v.into_iter().map(to_owned_cow).collect())
390            }
391            AttributeValue::GmailMsgId(v) => AttributeValue::GmailMsgId(v),
392            AttributeValue::GmailThrId(v) => AttributeValue::GmailThrId(v),
393        }
394    }
395}
396
397#[allow(clippy::large_enum_variant)]
398#[derive(Debug, Eq, PartialEq)]
399pub enum BodyStructure<'a> {
400    Basic {
401        common: BodyContentCommon<'a>,
402        other: BodyContentSinglePart<'a>,
403        extension: Option<BodyExtension<'a>>,
404    },
405    Text {
406        common: BodyContentCommon<'a>,
407        other: BodyContentSinglePart<'a>,
408        lines: u32,
409        extension: Option<BodyExtension<'a>>,
410    },
411    Message {
412        common: BodyContentCommon<'a>,
413        other: BodyContentSinglePart<'a>,
414        envelope: Envelope<'a>,
415        body: Box<BodyStructure<'a>>,
416        lines: u32,
417        extension: Option<BodyExtension<'a>>,
418    },
419    Multipart {
420        common: BodyContentCommon<'a>,
421        bodies: Vec<BodyStructure<'a>>,
422        extension: Option<BodyExtension<'a>>,
423    },
424}
425
426impl<'a> BodyStructure<'a> {
427    pub fn into_owned(self) -> BodyStructure<'static> {
428        match self {
429            BodyStructure::Basic {
430                common,
431                other,
432                extension,
433            } => BodyStructure::Basic {
434                common: common.into_owned(),
435                other: other.into_owned(),
436                extension: extension.map(|v| v.into_owned()),
437            },
438            BodyStructure::Text {
439                common,
440                other,
441                lines,
442                extension,
443            } => BodyStructure::Text {
444                common: common.into_owned(),
445                other: other.into_owned(),
446                lines,
447                extension: extension.map(|v| v.into_owned()),
448            },
449            BodyStructure::Message {
450                common,
451                other,
452                envelope,
453                body,
454                lines,
455                extension,
456            } => BodyStructure::Message {
457                common: common.into_owned(),
458                other: other.into_owned(),
459                envelope: envelope.into_owned(),
460                body: Box::new(body.into_owned()),
461                lines,
462                extension: extension.map(|v| v.into_owned()),
463            },
464            BodyStructure::Multipart {
465                common,
466                bodies,
467                extension,
468            } => BodyStructure::Multipart {
469                common: common.into_owned(),
470                bodies: bodies.into_iter().map(|v| v.into_owned()).collect(),
471                extension: extension.map(|v| v.into_owned()),
472            },
473        }
474    }
475}
476
477#[derive(Debug, Eq, PartialEq)]
478pub struct BodyContentCommon<'a> {
479    pub ty: ContentType<'a>,
480    pub disposition: Option<ContentDisposition<'a>>,
481    pub language: Option<Vec<Cow<'a, str>>>,
482    pub location: Option<Cow<'a, str>>,
483}
484
485impl<'a> BodyContentCommon<'a> {
486    pub fn into_owned(self) -> BodyContentCommon<'static> {
487        BodyContentCommon {
488            ty: self.ty.into_owned(),
489            disposition: self.disposition.map(|v| v.into_owned()),
490            language: self
491                .language
492                .map(|v| v.into_iter().map(to_owned_cow).collect()),
493            location: self.location.map(to_owned_cow),
494        }
495    }
496}
497
498#[derive(Debug, Eq, PartialEq)]
499pub struct BodyContentSinglePart<'a> {
500    pub id: Option<Cow<'a, str>>,
501    pub md5: Option<Cow<'a, str>>,
502    pub description: Option<Cow<'a, str>>,
503    pub transfer_encoding: ContentEncoding<'a>,
504    pub octets: u32,
505}
506
507impl<'a> BodyContentSinglePart<'a> {
508    pub fn into_owned(self) -> BodyContentSinglePart<'static> {
509        BodyContentSinglePart {
510            id: self.id.map(to_owned_cow),
511            md5: self.md5.map(to_owned_cow),
512            description: self.description.map(to_owned_cow),
513            transfer_encoding: self.transfer_encoding.into_owned(),
514            octets: self.octets,
515        }
516    }
517}
518
519#[derive(Debug, Eq, PartialEq)]
520pub struct ContentType<'a> {
521    pub ty: Cow<'a, str>,
522    pub subtype: Cow<'a, str>,
523    pub params: BodyParams<'a>,
524}
525
526impl<'a> ContentType<'a> {
527    pub fn into_owned(self) -> ContentType<'static> {
528        ContentType {
529            ty: to_owned_cow(self.ty),
530            subtype: to_owned_cow(self.subtype),
531            params: body_param_owned(self.params),
532        }
533    }
534}
535
536#[derive(Debug, Eq, PartialEq)]
537pub struct ContentDisposition<'a> {
538    pub ty: Cow<'a, str>,
539    pub params: BodyParams<'a>,
540}
541
542impl<'a> ContentDisposition<'a> {
543    pub fn into_owned(self) -> ContentDisposition<'static> {
544        ContentDisposition {
545            ty: to_owned_cow(self.ty),
546            params: body_param_owned(self.params),
547        }
548    }
549}
550
551#[derive(Debug, Eq, PartialEq)]
552pub enum ContentEncoding<'a> {
553    SevenBit,
554    EightBit,
555    Binary,
556    Base64,
557    QuotedPrintable,
558    Other(Cow<'a, str>),
559}
560
561impl<'a> ContentEncoding<'a> {
562    pub fn into_owned(self) -> ContentEncoding<'static> {
563        match self {
564            ContentEncoding::SevenBit => ContentEncoding::SevenBit,
565            ContentEncoding::EightBit => ContentEncoding::EightBit,
566            ContentEncoding::Binary => ContentEncoding::Binary,
567            ContentEncoding::Base64 => ContentEncoding::Base64,
568            ContentEncoding::QuotedPrintable => ContentEncoding::QuotedPrintable,
569            ContentEncoding::Other(v) => ContentEncoding::Other(to_owned_cow(v)),
570        }
571    }
572}
573
574#[derive(Debug, Eq, PartialEq)]
575pub enum BodyExtension<'a> {
576    Num(u32),
577    Str(Option<Cow<'a, str>>),
578    List(Vec<BodyExtension<'a>>),
579}
580
581impl<'a> BodyExtension<'a> {
582    pub fn into_owned(self) -> BodyExtension<'static> {
583        match self {
584            BodyExtension::Num(v) => BodyExtension::Num(v),
585            BodyExtension::Str(v) => BodyExtension::Str(v.map(to_owned_cow)),
586            BodyExtension::List(v) => {
587                BodyExtension::List(v.into_iter().map(|v| v.into_owned()).collect())
588            }
589        }
590    }
591}
592
593pub type BodyParams<'a> = Option<Vec<(Cow<'a, str>, Cow<'a, str>)>>;
594
595fn body_param_owned(v: BodyParams<'_>) -> BodyParams<'static> {
596    v.map(|v| {
597        v.into_iter()
598            .map(|(k, v)| (to_owned_cow(k), to_owned_cow(v)))
599            .collect()
600    })
601}
602
603/// An RFC 2822 envelope
604///
605/// See https://datatracker.ietf.org/doc/html/rfc2822#section-3.6 for more details.
606#[derive(Debug, Eq, PartialEq)]
607pub struct Envelope<'a> {
608    pub date: Option<Cow<'a, [u8]>>,
609    pub subject: Option<Cow<'a, [u8]>>,
610    /// Author of the message; mailbox responsible for writing the message
611    pub from: Option<Vec<Address<'a>>>,
612    /// Mailbox of the agent responsible for the message's transmission
613    pub sender: Option<Vec<Address<'a>>>,
614    /// Mailbox that the author of the message suggests replies be sent to
615    pub reply_to: Option<Vec<Address<'a>>>,
616    pub to: Option<Vec<Address<'a>>>,
617    pub cc: Option<Vec<Address<'a>>>,
618    pub bcc: Option<Vec<Address<'a>>>,
619    pub in_reply_to: Option<Cow<'a, [u8]>>,
620    pub message_id: Option<Cow<'a, [u8]>>,
621}
622
623impl<'a> Envelope<'a> {
624    pub fn into_owned(self) -> Envelope<'static> {
625        Envelope {
626            date: self.date.map(to_owned_cow),
627            subject: self.subject.map(to_owned_cow),
628            from: self
629                .from
630                .map(|v| v.into_iter().map(|v| v.into_owned()).collect()),
631            sender: self
632                .sender
633                .map(|v| v.into_iter().map(|v| v.into_owned()).collect()),
634            reply_to: self
635                .reply_to
636                .map(|v| v.into_iter().map(|v| v.into_owned()).collect()),
637            to: self
638                .to
639                .map(|v| v.into_iter().map(|v| v.into_owned()).collect()),
640            cc: self
641                .cc
642                .map(|v| v.into_iter().map(|v| v.into_owned()).collect()),
643            bcc: self
644                .bcc
645                .map(|v| v.into_iter().map(|v| v.into_owned()).collect()),
646            in_reply_to: self.in_reply_to.map(to_owned_cow),
647            message_id: self.message_id.map(to_owned_cow),
648        }
649    }
650}
651
652#[derive(Debug, Eq, PartialEq)]
653pub struct Address<'a> {
654    pub name: Option<Cow<'a, [u8]>>,
655    pub adl: Option<Cow<'a, [u8]>>,
656    pub mailbox: Option<Cow<'a, [u8]>>,
657    pub host: Option<Cow<'a, [u8]>>,
658}
659
660impl<'a> Address<'a> {
661    pub fn into_owned(self) -> Address<'static> {
662        Address {
663            name: self.name.map(to_owned_cow),
664            adl: self.adl.map(to_owned_cow),
665            mailbox: self.mailbox.map(to_owned_cow),
666            host: self.host.map(to_owned_cow),
667        }
668    }
669}
670
671#[derive(Clone, Debug, Eq, PartialEq)]
672pub struct RequestId(pub String);
673
674impl RequestId {
675    pub fn as_bytes(&self) -> &[u8] {
676        self.0.as_bytes()
677    }
678}
679
680#[derive(Clone, Copy, Debug, Eq, PartialEq)]
681pub enum State {
682    NotAuthenticated,
683    Authenticated,
684    Selected,
685    Logout,
686}
687
688// Body Structure
689
690pub struct BodyFields<'a> {
691    pub param: BodyParams<'a>,
692    pub id: Option<Cow<'a, str>>,
693    pub description: Option<Cow<'a, str>>,
694    pub transfer_encoding: ContentEncoding<'a>,
695    pub octets: u32,
696}
697
698impl<'a> BodyFields<'a> {
699    pub fn into_owned(self) -> BodyFields<'static> {
700        BodyFields {
701            param: body_param_owned(self.param),
702            id: self.id.map(to_owned_cow),
703            description: self.description.map(to_owned_cow),
704            transfer_encoding: self.transfer_encoding.into_owned(),
705            octets: self.octets,
706        }
707    }
708}
709
710pub struct BodyExt1Part<'a> {
711    pub md5: Option<Cow<'a, str>>,
712    pub disposition: Option<ContentDisposition<'a>>,
713    pub language: Option<Vec<Cow<'a, str>>>,
714    pub location: Option<Cow<'a, str>>,
715    pub extension: Option<BodyExtension<'a>>,
716}
717
718impl<'a> BodyExt1Part<'a> {
719    pub fn into_owned(self) -> BodyExt1Part<'static> {
720        BodyExt1Part {
721            md5: self.md5.map(to_owned_cow),
722            disposition: self.disposition.map(|v| v.into_owned()),
723            language: self
724                .language
725                .map(|v| v.into_iter().map(to_owned_cow).collect()),
726            location: self.location.map(to_owned_cow),
727            extension: self.extension.map(|v| v.into_owned()),
728        }
729    }
730}
731
732pub struct BodyExtMPart<'a> {
733    pub param: BodyParams<'a>,
734    pub disposition: Option<ContentDisposition<'a>>,
735    pub language: Option<Vec<Cow<'a, str>>>,
736    pub location: Option<Cow<'a, str>>,
737    pub extension: Option<BodyExtension<'a>>,
738}
739
740impl<'a> BodyExtMPart<'a> {
741    pub fn into_owned(self) -> BodyExtMPart<'static> {
742        BodyExtMPart {
743            param: body_param_owned(self.param),
744            disposition: self.disposition.map(|v| v.into_owned()),
745            language: self
746                .language
747                .map(|v| v.into_iter().map(to_owned_cow).collect()),
748            location: self.location.map(to_owned_cow),
749            extension: self.extension.map(|v| v.into_owned()),
750        }
751    }
752}
753
754/// The name attributes are returned as part of a LIST response described in
755/// [RFC 3501 section 7.2.2](https://tools.ietf.org/html/rfc3501#section-7.2.2).
756///
757/// This enumeration additional includes values from the extension Special-Use
758/// Mailboxes [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2).
759#[derive(Debug, Eq, PartialEq, Clone)]
760#[non_exhaustive]
761pub enum NameAttribute<'a> {
762    /// From [RFC 3501 section 7.2.2](https://tools.ietf.org/html/rfc3501#section-7.2.2):
763    ///
764    /// > It is not possible for any child levels of hierarchy to exist
765    /// > under this name; no child levels exist now and none can be
766    /// > created in the future.
767    NoInferiors,
768    /// From [RFC 3501 section 7.2.2](https://tools.ietf.org/html/rfc3501#section-7.2.2):
769    ///
770    /// > It is not possible to use this name as a selectable mailbox.
771    NoSelect,
772    /// From [RFC 3501 section 7.2.2](https://tools.ietf.org/html/rfc3501#section-7.2.2):
773    ///
774    /// > The mailbox has been marked "interesting" by the server; the
775    /// > mailbox probably contains messages that have been added since
776    /// > the last time the mailbox was selected.
777    Marked,
778    /// From [RFC 3501 section 7.2.2](https://tools.ietf.org/html/rfc3501#section-7.2.2):
779    ///
780    /// > The mailbox does not contain any additional messages since the
781    /// > last time the mailbox was selected.
782    Unmarked,
783    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2):
784    ///
785    /// > This mailbox presents all messages in the user's message store.
786    /// > Implementations MAY omit some messages, such as, perhaps, those
787    /// > in \Trash and \Junk.  When this special use is supported, it is
788    /// > almost certain to represent a virtual mailbox.
789    All,
790    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2):
791    ///
792    /// > This mailbox is used to archive messages.  The meaning of an
793    /// > "archival" mailbox is server-dependent; typically, it will be
794    /// > used to get messages out of the inbox, or otherwise keep them
795    /// > out of the user's way, while still making them accessible.
796    Archive,
797    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2):
798    ///
799    /// > This mailbox is used to hold draft messages -- typically,
800    /// > messages that are being composed but have not yet been sent.  In
801    /// > some server implementations, this might be a virtual mailbox,
802    /// > containing messages from other mailboxes that are marked with
803    /// > the "\Draft" message flag.  Alternatively, this might just be
804    /// > advice that a client put drafts here.
805    Drafts,
806    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2):
807    ///
808    /// > This mailbox presents all messages marked in some way as
809    /// > "important".  When this special use is supported, it is likely
810    /// > to represent a virtual mailbox collecting messages (from other
811    /// > mailboxes) that are marked with the "\Flagged" message flag.
812    Flagged,
813    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2):
814    ///
815    /// > This mailbox is where messages deemed to be junk mail are held.
816    /// > Some server implementations might put messages here
817    /// > automatically.  Alternatively, this might just be advice to a
818    /// > client-side spam filter.
819    Junk,
820    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2):
821    ///
822    /// > This mailbox is used to hold copies of messages that have been
823    /// > sent.  Some server implementations might put messages here
824    /// > automatically.  Alternatively, this might just be advice that a
825    /// > client save sent messages here.
826    Sent,
827    /// From [RFC 6154 section 2](https://tools.ietf.org/html/rfc6154#section-2)
828    ///
829    /// > This mailbox is used to hold messages that have been deleted or
830    /// > marked for deletion.  In some server implementations, this might
831    /// > be a virtual mailbox, containing messages from other mailboxes
832    /// > that are marked with the "\Deleted" message flag.
833    /// > Alternatively, this might just be advice that a client that
834    /// > chooses not to use the IMAP "\Deleted" model should use this as
835    /// > its trash location.  In server implementations that strictly
836    /// > expect the IMAP "\Deleted" model, this special use is likely not
837    /// > to be supported.
838    Trash,
839    /// A name attribute not defined in [RFC 3501 section 7.2.2](https://tools.ietf.org/html/rfc3501#section-7.2.2)
840    /// or any supported extension.
841    Extension(Cow<'a, str>),
842}
843
844impl<'a> NameAttribute<'a> {
845    pub fn into_owned(self) -> NameAttribute<'static> {
846        match self {
847            // RFC 3501
848            NameAttribute::NoInferiors => NameAttribute::NoInferiors,
849            NameAttribute::NoSelect => NameAttribute::NoSelect,
850            NameAttribute::Marked => NameAttribute::Marked,
851            NameAttribute::Unmarked => NameAttribute::Unmarked,
852            // RFC 6154
853            NameAttribute::All => NameAttribute::All,
854            NameAttribute::Archive => NameAttribute::Archive,
855            NameAttribute::Drafts => NameAttribute::Drafts,
856            NameAttribute::Flagged => NameAttribute::Flagged,
857            NameAttribute::Junk => NameAttribute::Junk,
858            NameAttribute::Sent => NameAttribute::Sent,
859            NameAttribute::Trash => NameAttribute::Trash,
860            // Extensions not supported by this crate
861            NameAttribute::Extension(s) => NameAttribute::Extension(to_owned_cow(s)),
862        }
863    }
864}
865
866// IMAP4 QUOTA extension (rfc2087)
867
868/// https://tools.ietf.org/html/rfc2087#section-3
869#[derive(Debug, Eq, PartialEq, Hash, Clone)]
870pub enum QuotaResourceName<'a> {
871    /// Sum of messages' RFC822.SIZE, in units of 1024 octets
872    Storage,
873    /// Number of messages
874    Message,
875    Atom(Cow<'a, str>),
876}
877
878impl<'a> QuotaResourceName<'a> {
879    pub fn into_owned(self) -> QuotaResourceName<'static> {
880        match self {
881            QuotaResourceName::Message => QuotaResourceName::Message,
882            QuotaResourceName::Storage => QuotaResourceName::Storage,
883            QuotaResourceName::Atom(v) => QuotaResourceName::Atom(to_owned_cow(v)),
884        }
885    }
886}
887
888/// 5.1. QUOTA Response (https://tools.ietf.org/html/rfc2087#section-5.1)
889#[derive(Debug, Eq, PartialEq, Hash, Clone)]
890pub struct QuotaResource<'a> {
891    pub name: QuotaResourceName<'a>,
892    /// current usage of the resource
893    pub usage: u64,
894    /// resource limit
895    pub limit: u64,
896}
897
898impl<'a> QuotaResource<'a> {
899    pub fn into_owned(self) -> QuotaResource<'static> {
900        QuotaResource {
901            name: self.name.into_owned(),
902            usage: self.usage,
903            limit: self.limit,
904        }
905    }
906}
907
908/// 5.1. QUOTA Response (https://tools.ietf.org/html/rfc2087#section-5.1)
909#[derive(Debug, Eq, PartialEq, Hash, Clone)]
910pub struct Quota<'a> {
911    /// quota root name
912    pub root_name: Cow<'a, str>,
913    pub resources: Vec<QuotaResource<'a>>,
914}
915
916impl<'a> Quota<'a> {
917    pub fn into_owned(self) -> Quota<'static> {
918        Quota {
919            root_name: to_owned_cow(self.root_name),
920            resources: self.resources.into_iter().map(|r| r.into_owned()).collect(),
921        }
922    }
923}
924
925/// 5.2. QUOTAROOT Response (https://tools.ietf.org/html/rfc2087#section-5.2)
926#[derive(Debug, Eq, PartialEq, Hash, Clone)]
927pub struct QuotaRoot<'a> {
928    /// mailbox name
929    pub mailbox_name: Cow<'a, str>,
930    /// zero or more quota root names
931    pub quota_root_names: Vec<Cow<'a, str>>,
932}
933
934impl<'a> QuotaRoot<'a> {
935    pub fn into_owned(self) -> QuotaRoot<'static> {
936        QuotaRoot {
937            mailbox_name: to_owned_cow(self.mailbox_name),
938            quota_root_names: self
939                .quota_root_names
940                .into_iter()
941                .map(to_owned_cow)
942                .collect(),
943        }
944    }
945}
946
947#[cfg(test)]
948mod tests {
949    use super::*;
950
951    /// Tests that the [`NameAttribute::into_owned`] method returns the
952    /// same value (the ownership should only change).
953    #[test]
954    fn test_name_attribute_into_owned() {
955        let name_attributes = [
956            // RFC 3501
957            NameAttribute::NoInferiors,
958            NameAttribute::NoSelect,
959            NameAttribute::Marked,
960            NameAttribute::Unmarked,
961            // RFC 6154
962            NameAttribute::All,
963            NameAttribute::Archive,
964            NameAttribute::Drafts,
965            NameAttribute::Flagged,
966            NameAttribute::Junk,
967            NameAttribute::Sent,
968            NameAttribute::Trash,
969            // Extensions not supported by this crate
970            NameAttribute::Extension(Cow::Borrowed("Foobar")),
971        ];
972
973        for name_attribute in name_attributes {
974            let owned_name_attribute = name_attribute.clone().into_owned();
975            assert_eq!(name_attribute, owned_name_attribute);
976        }
977    }
978}