Skip to main content

imap_client/tasks/tasks/
store.rs

1use std::{collections::HashMap, num::NonZeroU32};
2
3use imap_next::imap_types::{
4    command::CommandBody,
5    core::Vec1,
6    fetch::MessageDataItem,
7    flag::{Flag, StoreResponse, StoreType},
8    response::{Data, StatusBody, StatusKind},
9    sequence::SequenceSet,
10};
11use tracing::debug;
12
13use super::TaskError;
14use crate::tasks::Task;
15
16/// Alter message data.
17#[derive(Clone, Debug)]
18pub struct StoreTask {
19    sequence_set: SequenceSet,
20    kind: StoreType,
21    flags: Vec<Flag<'static>>,
22    uid: bool,
23    output: HashMap<NonZeroU32, Vec1<MessageDataItem<'static>>>,
24}
25
26impl StoreTask {
27    pub fn new(sequence_set: SequenceSet, kind: StoreType, flags: Vec<Flag<'static>>) -> Self {
28        Self {
29            sequence_set,
30            kind,
31            flags,
32            uid: true,
33            output: Default::default(),
34        }
35    }
36
37    pub fn set_uid(&mut self, uid: bool) {
38        self.uid = uid;
39    }
40
41    pub fn with_uid(mut self, uid: bool) -> Self {
42        self.set_uid(uid);
43        self
44    }
45
46    pub fn silent(self) -> SilentStoreTask {
47        SilentStoreTask::new(self)
48    }
49}
50
51impl Task for StoreTask {
52    type Output = Result<HashMap<NonZeroU32, Vec1<MessageDataItem<'static>>>, TaskError>;
53
54    fn command_body(&self) -> CommandBody<'static> {
55        CommandBody::Store {
56            modifiers: Default::default(),
57            sequence_set: self.sequence_set.clone(),
58            kind: self.kind,
59            response: StoreResponse::Answer,
60            flags: self.flags.clone(),
61            uid: self.uid,
62        }
63    }
64
65    fn process_data(&mut self, data: Data<'static>) -> Option<Data<'static>> {
66        if let Data::Fetch { items, seq } = data {
67            if let Some(items) = self.output.insert(seq, items) {
68                debug!(seq, ?items, "received duplicate items");
69            }
70
71            None
72        } else {
73            Some(data)
74        }
75    }
76
77    fn process_tagged(self, status_body: StatusBody<'static>) -> Self::Output {
78        match status_body.kind {
79            StatusKind::Ok => Ok(self.output),
80            StatusKind::No => Err(TaskError::UnexpectedNoResponse(status_body)),
81            StatusKind::Bad => Err(TaskError::UnexpectedBadResponse(status_body)),
82        }
83    }
84}
85
86/// Alter message data instructing the server to not send the updated values.
87///
88/// Note: Same as [`StoreTask`], except that it does not return any output.
89#[derive(Clone, Debug)]
90pub struct SilentStoreTask(StoreTask);
91
92impl SilentStoreTask {
93    pub fn new(store: StoreTask) -> Self {
94        Self(store)
95    }
96}
97
98impl Task for SilentStoreTask {
99    type Output = Result<(), TaskError>;
100
101    fn command_body(&self) -> CommandBody<'static> {
102        CommandBody::Store {
103            modifiers: Default::default(),
104            sequence_set: self.0.sequence_set.clone(),
105            kind: self.0.kind,
106            response: StoreResponse::Silent,
107            flags: self.0.flags.clone(),
108            uid: self.0.uid,
109        }
110    }
111
112    fn process_tagged(self, status_body: StatusBody<'static>) -> Self::Output {
113        match status_body.kind {
114            StatusKind::Ok => Ok(()),
115            StatusKind::No => Err(TaskError::UnexpectedNoResponse(status_body)),
116            StatusKind::Bad => Err(TaskError::UnexpectedBadResponse(status_body)),
117        }
118    }
119}