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::warn;
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            sequence_set: self.sequence_set.clone(),
57            kind: self.kind,
58            response: StoreResponse::Answer,
59            flags: self.flags.clone(),
60            uid: self.uid,
61        }
62    }
63
64    fn process_data(&mut self, data: Data<'static>) -> Option<Data<'static>> {
65        if let Data::Fetch { items, seq } = data {
66            if let Some(items) = self.output.insert(seq, items) {
67                warn!(seq, ?items, "received duplicate items");
68            }
69
70            None
71        } else {
72            Some(data)
73        }
74    }
75
76    fn process_tagged(self, status_body: StatusBody<'static>) -> Self::Output {
77        match status_body.kind {
78            StatusKind::Ok => Ok(self.output),
79            StatusKind::No => Err(TaskError::UnexpectedNoResponse(status_body)),
80            StatusKind::Bad => Err(TaskError::UnexpectedBadResponse(status_body)),
81        }
82    }
83}
84
85/// Alter message data instructing the server to not send the updated values.
86///
87/// Note: Same as [`StoreTask`], except that it does not return any output.
88#[derive(Clone, Debug)]
89pub struct SilentStoreTask(StoreTask);
90
91impl SilentStoreTask {
92    pub fn new(store: StoreTask) -> Self {
93        Self(store)
94    }
95}
96
97impl Task for SilentStoreTask {
98    type Output = Result<(), TaskError>;
99
100    fn command_body(&self) -> CommandBody<'static> {
101        CommandBody::Store {
102            sequence_set: self.0.sequence_set.clone(),
103            kind: self.0.kind,
104            response: StoreResponse::Silent,
105            flags: self.0.flags.clone(),
106            uid: self.0.uid,
107        }
108    }
109
110    fn process_tagged(self, status_body: StatusBody<'static>) -> Self::Output {
111        match status_body.kind {
112            StatusKind::Ok => Ok(()),
113            StatusKind::No => Err(TaskError::UnexpectedNoResponse(status_body)),
114            StatusKind::Bad => Err(TaskError::UnexpectedBadResponse(status_body)),
115        }
116    }
117}