imap_client/tasks/tasks/
append.rs

1use imap_next::imap_types::{
2    command::CommandBody,
3    datetime::DateTime,
4    extensions::binary::LiteralOrLiteral8,
5    flag::Flag,
6    mailbox::Mailbox,
7    response::{Data, StatusBody, StatusKind},
8};
9use tracing::warn;
10
11use super::TaskError;
12use crate::tasks::Task;
13
14#[derive(Clone, Debug)]
15pub struct AppendTask {
16    mailbox: Mailbox<'static>,
17    flags: Vec<Flag<'static>>,
18    date: Option<DateTime>,
19    message: LiteralOrLiteral8<'static>,
20    output: Option<u32>,
21}
22
23impl AppendTask {
24    pub fn new(mailbox: Mailbox<'static>, message: LiteralOrLiteral8<'static>) -> Self {
25        Self {
26            mailbox,
27            flags: Default::default(),
28            date: Default::default(),
29            message,
30            output: Default::default(),
31        }
32    }
33
34    pub fn set_flags(&mut self, flags: Vec<Flag<'static>>) {
35        self.flags = flags;
36    }
37
38    pub fn add_flag(&mut self, flag: Flag<'static>) {
39        self.flags.push(flag);
40    }
41
42    pub fn with_flags(mut self, flags: Vec<Flag<'static>>) -> Self {
43        self.set_flags(flags);
44        self
45    }
46
47    pub fn with_flag(mut self, flag: Flag<'static>) -> Self {
48        self.add_flag(flag);
49        self
50    }
51
52    pub fn set_date(&mut self, date: DateTime) {
53        self.date = Some(date);
54    }
55
56    pub fn with_date(mut self, date: DateTime) -> Self {
57        self.set_date(date);
58        self
59    }
60}
61
62impl Task for AppendTask {
63    type Output = Result<Option<u32>, TaskError>;
64
65    fn command_body(&self) -> CommandBody<'static> {
66        CommandBody::Append {
67            mailbox: self.mailbox.clone(),
68            flags: self.flags.clone(),
69            date: self.date.clone(),
70            message: self.message.clone(),
71        }
72    }
73
74    fn process_data(&mut self, data: Data<'static>) -> Option<Data<'static>> {
75        // In case the mailbox is already selected, we should receive
76        // an `EXISTS` response.
77        if let Data::Exists(seq) = data {
78            if self.output.is_some() {
79                warn!("received duplicate APPEND EXISTS data");
80            }
81            self.output = Some(seq);
82            None
83        } else {
84            Some(data)
85        }
86    }
87
88    fn process_tagged(self, status_body: StatusBody<'static>) -> Self::Output {
89        match status_body.kind {
90            StatusKind::Ok => Ok(self.output),
91            StatusKind::No => Err(TaskError::UnexpectedNoResponse(status_body)),
92            StatusKind::Bad => Err(TaskError::UnexpectedBadResponse(status_body)),
93        }
94    }
95}
96
97/// Special [`NoOpTask`](super::noop::NoOpTask) that captures `EXISTS`
98/// responses.
99///
100/// This task should be used whenever [`AppendTask`] does not return
101/// the number of messages in the mailbox the appended message
102/// resides.
103#[derive(Clone, Debug, Default)]
104pub struct PostAppendNoOpTask {
105    output: Option<u32>,
106}
107
108impl PostAppendNoOpTask {
109    pub fn new() -> Self {
110        Default::default()
111    }
112}
113
114impl Task for PostAppendNoOpTask {
115    type Output = Result<Option<u32>, TaskError>;
116
117    fn command_body(&self) -> CommandBody<'static> {
118        CommandBody::Noop
119    }
120
121    fn process_data(&mut self, data: Data<'static>) -> Option<Data<'static>> {
122        if let Data::Exists(seq) = data {
123            self.output = Some(seq);
124            None
125        } else {
126            Some(data)
127        }
128    }
129
130    fn process_tagged(self, status_body: StatusBody<'static>) -> Self::Output {
131        match status_body.kind {
132            StatusKind::Ok => Ok(self.output),
133            StatusKind::No => Err(TaskError::UnexpectedNoResponse(status_body)),
134            StatusKind::Bad => Err(TaskError::UnexpectedBadResponse(status_body)),
135        }
136    }
137}
138
139/// Special [`CheckTask`](super::check::CheckTask) that captures
140/// `EXISTS` responses.
141///
142/// This task should be used whenever [`AppendTask`] and
143/// [`PostAppendNoOpTask`] do not return the number of messages in the
144/// mailbox the appended message resides.
145#[derive(Clone, Debug, Default)]
146pub struct PostAppendCheckTask {
147    output: Option<u32>,
148}
149
150impl PostAppendCheckTask {
151    pub fn new() -> Self {
152        Default::default()
153    }
154}
155
156impl Task for PostAppendCheckTask {
157    type Output = Result<Option<u32>, TaskError>;
158
159    fn command_body(&self) -> CommandBody<'static> {
160        CommandBody::Check
161    }
162
163    fn process_data(&mut self, data: Data<'static>) -> Option<Data<'static>> {
164        if let Data::Exists(seq) = data {
165            self.output = Some(seq);
166            None
167        } else {
168            Some(data)
169        }
170    }
171
172    fn process_tagged(self, status_body: StatusBody<'static>) -> Self::Output {
173        match status_body.kind {
174            StatusKind::Ok => Ok(self.output),
175            StatusKind::No => Err(TaskError::UnexpectedNoResponse(status_body)),
176            StatusKind::Bad => Err(TaskError::UnexpectedBadResponse(status_body)),
177        }
178    }
179}