io_email/flag/m2dir/
store.rs1use alloc::{collections::VecDeque, string::String};
18use std::path::PathBuf;
19
20use io_m2dir::{
21 coroutine::*,
22 flag::{
23 add::{
24 M2dirFlagAdd as InnerAdd, M2dirFlagAddError as AddErr, M2dirFlagAddOptions as AddOpts,
25 },
26 remove::{
27 M2dirFlagRemove as InnerRemove, M2dirFlagRemoveError as RemoveErr,
28 M2dirFlagRemoveOptions as RemoveOpts,
29 },
30 set::{
31 M2dirFlagSet as InnerSet, M2dirFlagSetError as SetErr, M2dirFlagSetOptions as SetOpts,
32 },
33 types::M2dirFlags,
34 },
35 m2dir::types::M2dir,
36};
37use log::trace;
38use thiserror::Error;
39
40use crate::{
41 flag::types::{Flag, FlagOp},
42 m2dir::convert::{InvalidMailboxName, flags_to_m2dir, resolve_mailbox},
43};
44
45#[derive(Debug, Error)]
47pub enum M2dirFlagStoreError {
48 #[error(transparent)]
49 Add(#[from] AddErr),
50 #[error(transparent)]
51 Set(#[from] SetErr),
52 #[error(transparent)]
53 Remove(#[from] RemoveErr),
54 #[error(transparent)]
55 InvalidMailbox(#[from] InvalidMailboxName),
56}
57
58pub struct M2dirFlagStore {
60 m2dir: M2dir,
61 flags: M2dirFlags,
62 op: FlagOp,
63 pending: VecDeque<String>,
64 current: Option<Stage>,
65}
66
67impl M2dirFlagStore {
68 pub fn new(
69 root: impl Into<PathBuf>,
70 mailbox: &str,
71 ids: &[&str],
72 flags: &[Flag],
73 op: FlagOp,
74 ) -> Result<Self, M2dirFlagStoreError> {
75 trace!("prepare m2dir flag store ({op:?})");
76 let m2dir = resolve_mailbox(root, mailbox)?;
77 Ok(Self {
78 m2dir,
79 flags: flags_to_m2dir(flags),
80 op,
81 pending: ids.iter().map(|s| (*s).into()).collect(),
82 current: None,
83 })
84 }
85}
86
87enum Stage {
88 Add(InnerAdd),
89 Set(InnerSet),
90 Remove(InnerRemove),
91}
92
93impl Stage {
94 fn start(m2dir: &M2dir, id: String, flags: M2dirFlags, op: FlagOp) -> Self {
95 match op {
96 FlagOp::Add => Stage::Add(InnerAdd::new(m2dir, id, flags, AddOpts::default())),
97 FlagOp::Set => Stage::Set(InnerSet::new(m2dir, id, flags, SetOpts::default())),
98 FlagOp::Remove => {
99 Stage::Remove(InnerRemove::new(m2dir, id, flags, RemoveOpts::default()))
100 }
101 }
102 }
103}
104
105impl M2dirCoroutine for M2dirFlagStore {
106 type Yield = M2dirYield;
107 type Return = Result<(), M2dirFlagStoreError>;
108
109 fn resume(&mut self, arg: Option<M2dirArg>) -> M2dirCoroutineState<Self::Yield, Self::Return> {
110 let mut arg = arg;
111 loop {
112 if self.current.is_none() {
113 let Some(id) = self.pending.pop_front() else {
114 return M2dirCoroutineState::Complete(Ok(()));
115 };
116 self.current = Some(Stage::start(&self.m2dir, id, self.flags.clone(), self.op));
117 }
118 let stage = self.current.as_mut().unwrap();
119 let inner_state = match stage {
120 Stage::Add(inner) => match inner.resume(arg.take()) {
121 M2dirCoroutineState::Yielded(y) => M2dirCoroutineState::Yielded(y),
122 M2dirCoroutineState::Complete(r) => {
123 M2dirCoroutineState::Complete(r.map_err(M2dirFlagStoreError::from))
124 }
125 },
126 Stage::Set(inner) => match inner.resume(arg.take()) {
127 M2dirCoroutineState::Yielded(y) => M2dirCoroutineState::Yielded(y),
128 M2dirCoroutineState::Complete(r) => {
129 M2dirCoroutineState::Complete(r.map_err(M2dirFlagStoreError::from))
130 }
131 },
132 Stage::Remove(inner) => match inner.resume(arg.take()) {
133 M2dirCoroutineState::Yielded(y) => M2dirCoroutineState::Yielded(y),
134 M2dirCoroutineState::Complete(r) => {
135 M2dirCoroutineState::Complete(r.map_err(M2dirFlagStoreError::from))
136 }
137 },
138 };
139 match inner_state {
140 M2dirCoroutineState::Yielded(y) => return M2dirCoroutineState::Yielded(y),
141 M2dirCoroutineState::Complete(Ok(())) => {
142 self.current = None;
143 }
144 M2dirCoroutineState::Complete(Err(err)) => {
145 return M2dirCoroutineState::Complete(Err(err));
146 }
147 }
148 }
149 }
150}