Skip to content

pimalaya/io-jmap

I/O JMAP Documentation Matrix Mastodon

I/O-free JMAP client library written in Rust, based on io-http

Table of contents

RFC coverage

This library implements JMAP as I/O-agnostic coroutines — no sockets, no async runtime, no std required by the protocol layer.

RFC What it covers
8620 JMAP core — session discovery, API requests, Foo/get, Foo/set, Foo/query, Foo/changes, blobs
8621 JMAP for Mail — Mailbox, Email, Thread, Identity, EmailSubmission, VacationResponse

Examples

Fetch a JMAP session (blocking)

use std::{net::TcpStream, sync::Arc};

use io_jmap::rfc8620::session_get::{JmapSessionGet, JmapSessionGetResult};
use io_socket::runtimes::std_stream::handle;
use rustls::{ClientConfig, ClientConnection, StreamOwned};
use rustls_platform_verifier::ConfigVerifierExt;
use secrecy::SecretString;
use url::Url;

let http_auth = SecretString::from("Bearer your-token-here");
let base_url = Url::parse("https://api.fastmail.com/jmap/session/").unwrap();

let config = ClientConfig::with_platform_verifier().unwrap();
let server_name = base_url.host_str().unwrap().to_string().try_into().unwrap();
let conn = ClientConnection::new(Arc::new(config), server_name).unwrap();
let tcp = TcpStream::connect((base_url.host_str().unwrap(), 443)).unwrap();
let mut stream = StreamOwned::new(conn, tcp);

let mut coroutine = JmapSessionGet::new(&http_auth, &base_url);
let mut arg = None;

let session = loop {
    match coroutine.resume(arg.take()) {
        JmapSessionGetResult::Ok { session, .. } => break session,
        JmapSessionGetResult::Io { input } => arg = Some(handle(&mut stream, input).unwrap()),
        JmapSessionGetResult::Redirect { url, .. } => {
            // reconnect to the new URL and retry
            todo!("reconnect to {url}")
        }
        JmapSessionGetResult::Err { err } => panic!("{err}"),
    }
};

println!("Logged in as: {}", session.username);
println!("API URL: {}", session.api_url);

List mailboxes (blocking)

use std::{net::TcpStream, sync::Arc};

use io_jmap::rfc8621::mailbox_query::{JmapMailboxQuery, JmapMailboxQueryResult};
use io_socket::runtimes::std_stream::handle;
use rustls::{ClientConfig, ClientConnection, StreamOwned};
use rustls_platform_verifier::ConfigVerifierExt;
use secrecy::SecretString;

// (session obtained from JmapSessionGet above)

let config = ClientConfig::with_platform_verifier().unwrap();
let server_name = session.api_url.host_str().unwrap().to_string().try_into().unwrap();
let conn = ClientConnection::new(Arc::new(config), server_name).unwrap();
let tcp = TcpStream::connect((session.api_url.host_str().unwrap(), 443)).unwrap();
let mut stream = StreamOwned::new(conn, tcp);

let mut coroutine = JmapMailboxQuery::new(&session, &http_auth, None, None, None, None, None).unwrap();
let mut arg = None;

let mailboxes = loop {
    match coroutine.resume(arg.take()) {
        JmapMailboxQueryResult::Ok { mailboxes, .. } => break mailboxes,
        JmapMailboxQueryResult::Io { input } => arg = Some(handle(&mut stream, input).unwrap()),
        JmapMailboxQueryResult::Err { err } => panic!("{err}"),
    }
};

for mailbox in &mailboxes {
    println!("{:?} — {:?}", mailbox.role, mailbox.name);
}

More examples

Have a look at projects built on 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