use alloc::{string::String, vec::Vec};
use core::mem;
use std::path::PathBuf;
use io_m2dir::{
coroutine::*,
entry::store::{
M2dirEntryStore as InnerStore, M2dirEntryStoreError as StoreErr,
M2dirEntryStoreOptions as StoreOpts,
},
flag::{
set::{
M2dirFlagSet as InnerFlagSet, M2dirFlagSetError as FlagSetErr,
M2dirFlagSetOptions as FlagSetOpts,
},
types::M2dirFlags,
},
m2dir::types::M2dir,
};
use log::trace;
use thiserror::Error;
use crate::{
flag::types::Flag,
m2dir::convert::{InvalidMailboxName, flags_to_m2dir, resolve_mailbox},
};
#[derive(Debug, Error)]
pub enum M2dirMessageAddError {
#[error(transparent)]
Store(#[from] StoreErr),
#[error(transparent)]
SetFlags(#[from] FlagSetErr),
#[error(transparent)]
InvalidMailbox(#[from] InvalidMailboxName),
}
pub struct M2dirMessageAdd {
state: State,
m2dir: M2dir,
flags: M2dirFlags,
}
impl M2dirMessageAdd {
pub fn new(
root: impl Into<PathBuf>,
mailbox: &str,
flags: &[Flag],
bytes: Vec<u8>,
) -> Result<Self, M2dirMessageAddError> {
trace!("prepare m2dir message add");
let m2dir = resolve_mailbox(root, mailbox)?;
let store = InnerStore::new(m2dir.clone(), bytes, StoreOpts::default());
Ok(Self {
state: State::Storing(store),
m2dir,
flags: flags_to_m2dir(flags),
})
}
}
enum State {
Storing(InnerStore),
SettingFlags { set: InnerFlagSet, id: String },
Done,
}
impl M2dirCoroutine for M2dirMessageAdd {
type Yield = M2dirYield;
type Return = Result<String, M2dirMessageAddError>;
fn resume(&mut self, arg: Option<M2dirArg>) -> M2dirCoroutineState<Self::Yield, Self::Return> {
match mem::replace(&mut self.state, State::Done) {
State::Storing(mut store) => match store.resume(arg) {
M2dirCoroutineState::Yielded(y) => {
self.state = State::Storing(store);
M2dirCoroutineState::Yielded(y)
}
M2dirCoroutineState::Complete(Ok(entry)) => {
if self.flags.is_empty() {
M2dirCoroutineState::Complete(Ok(entry.id().into()))
} else {
let id: String = entry.id().into();
let set = InnerFlagSet::new(
&self.m2dir,
&id,
self.flags.clone(),
FlagSetOpts::default(),
);
self.state = State::SettingFlags { set, id };
M2dirCoroutine::resume(self, None)
}
}
M2dirCoroutineState::Complete(Err(err)) => {
M2dirCoroutineState::Complete(Err(err.into()))
}
},
State::SettingFlags { mut set, id } => match set.resume(arg) {
M2dirCoroutineState::Yielded(y) => {
self.state = State::SettingFlags { set, id };
M2dirCoroutineState::Yielded(y)
}
M2dirCoroutineState::Complete(Ok(())) => M2dirCoroutineState::Complete(Ok(id)),
M2dirCoroutineState::Complete(Err(err)) => {
M2dirCoroutineState::Complete(Err(err.into()))
}
},
State::Done => unreachable!("M2dirMessageAdd resumed after completion"),
}
}
}