I/O-free Maildir library written in Rust
This library provides I/O-agnostic coroutines to manage Maildir filesystems. It is built on three concepts:
A coroutine is an I/O-free, resumable and composable state machine. It emits filesystem requests (Wants* variants such as WantsDirCreate, WantsFileRead, WantsRename) without performing any I/O itself, and receives filesystem responses (FsOutput) to make progress. A coroutine is terminated when it stops emitting requests.
See available coroutines at ./src/coroutines.
The caller drives the coroutine forward. On each Wants* result, it performs the requested filesystem operation (e.g. std::fs::create_dir, std::fs::read, std::fs::rename) and feeds the matching FsOutput variant back into the next resume call.
The loop is the glue between coroutine and caller. It alternates between resume and the actual filesystem call until the coroutine terminates.
use std::{
collections::{BTreeMap, BTreeSet},
fs::{self, File},
io::Write,
path::PathBuf,
};
use io_maildir::{
coroutines::{
maildir_create::{MaildirCreate, MaildirCreateResult},
message_store::{MaildirMessageStore, MaildirMessageStoreResult},
},
flag::Flags,
io::FsOutput,
maildir::{Maildir, MaildirSubdir},
};
let root = PathBuf::from("/path/to/maildir");
// create the Maildir structure (root, cur, new, tmp)
let mut arg = None;
let mut create = MaildirCreate::new(root.clone());
loop {
match create.resume(arg.take()) {
MaildirCreateResult::Ok => break,
MaildirCreateResult::WantsDirCreate(paths) => {
for path in paths {
fs::create_dir(&path).unwrap();
}
arg = Some(FsOutput::DirCreate);
}
MaildirCreateResult::Err(err) => panic!("{err}"),
}
}
let maildir = Maildir::try_from(root).unwrap();
// store a message in /new
let contents = b"From: alice@example.com\r\nSubject: Hello\r\n\r\nHello!\r\n".to_vec();
let mut arg = None;
let mut store = MaildirMessageStore::new(
maildir,
MaildirSubdir::New,
Flags::default(),
contents,
);
let (id, path) = loop {
match store.resume(arg.take()) {
MaildirMessageStoreResult::Ok { id, path } => break (id, path),
MaildirMessageStoreResult::WantsFileCreate(files) => {
for (path, bytes) in files {
File::create(&path).unwrap().write_all(&bytes).unwrap();
}
arg = Some(FsOutput::FileCreate);
}
MaildirMessageStoreResult::WantsRename(pairs) => {
for (from, to) in pairs {
fs::rename(&from, &to).unwrap();
}
arg = Some(FsOutput::Rename);
}
MaildirMessageStoreResult::Err(err) => panic!("{err}"),
}
};
println!("stored {id} at {}", path.display());See complete examples at ./examples.
Have a look at projects built on the top of this library:
- himalaya: CLI to manage emails
This project is licensed under either of:
at your option.
- Chat on Matrix
- News on Mastodon or RSS
- Mail at pimalaya.org@posteo.net
Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:
- 2022: NGI Assure
- 2023: NGI Zero Entrust
- 2024: NGI Zero Core (still ongoing in 2026)
If you appreciate the project, feel free to donate using one of the following providers:
