Skip to content

pimalaya/io-maildir

I/O Maildir Documentation Matrix Mastodon

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:

Coroutine

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.

Caller

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.

Loop

The loop is the glue between coroutine and caller. It alternates between resume and the actual filesystem call until the coroutine terminates.

Examples

Create a Maildir and store a message (blocking)

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.

More examples

Have a look at projects built on the top of this library:

License

This project is licensed under either of:

at your option.

Social

Sponsoring

nlnet

Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:

If you appreciate the project, feel free to donate using one of the following providers:

GitHub Ko-fi Buy Me a Coffee Liberapay thanks.dev PayPal

About

Set of I/O-free coroutines to manage Maildir filesystems

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Contributing

Security policy

Stars

Watchers

Forks

Contributors