Expand description
§mailrs-jmap
Server-side JMAP (RFC 8620 + RFC 8621) dispatcher and method handlers, decoupled from any specific web framework or backing store.
Plug your store in by implementing MailStore (async, object-safe), then
call dispatch_request / dispatch_method from your HTTP handler.
§What’s covered
Mailbox/get,Mailbox/query(RFC 8621 §2)Email/get,Email/query,Email/set(RFC 8621 §4)Thread/get(RFC 8621 §3)EmailSubmission/set(RFC 8621 §7) — create only- Method back-references (RFC 8620 §3.7)
§What’s not
- JMAP push (event source / WebSocket) — the wire format is up to you because event sourcing is so deeply coupled to your runtime; this crate stays out of it.
- JMAP-Contacts, JMAP-Calendars — different spec families.
- Server-side capabilities object — you choose what to advertise; the
JMAP_*_CAPconstants give you the canonical URIs.
§Quick start
use async_trait::async_trait;
use mailrs_jmap::{
dispatch_request, JmapRequest, MailStore,
types::{Mailbox, MailboxCounts, Message, ParsedBody, SubmissionResult},
};
struct MyStore;
#[async_trait]
impl MailStore for MyStore {
async fn list_mailboxes(&self, _user: &str) -> Result<Vec<Mailbox>, mailrs_jmap::store::StoreError> {
Ok(vec![])
}
async fn mailbox_status(&self, _id: i64) -> Result<MailboxCounts, mailrs_jmap::store::StoreError> {
Ok(MailboxCounts::default())
}
async fn list_messages(&self, _: i64, _: u32, _: u32) -> Result<Vec<Message>, mailrs_jmap::store::StoreError> { Ok(vec![]) }
async fn get_message_by_db_id(&self, _: &str, _: i64) -> Result<Option<Message>, mailrs_jmap::store::StoreError> { Ok(None) }
async fn list_thread_messages(&self, _: &str, _: &str) -> Result<Vec<Message>, mailrs_jmap::store::StoreError> { Ok(vec![]) }
async fn update_flags(&self, _: i64, _: u32, _: u32) -> Result<(), mailrs_jmap::store::StoreError> { Ok(()) }
async fn add_flags(&self, _: i64, _: u32, _: u32) -> Result<(), mailrs_jmap::store::StoreError> { Ok(()) }
async fn read_message_raw(&self, _: &Message) -> Option<Vec<u8>> { None }
fn parse_message(&self, _: &[u8]) -> ParsedBody { ParsedBody::default() }
async fn submit_message(&self, _: &str, _: &Message, _: &[u8]) -> SubmissionResult {
SubmissionResult { success: false, message: Some("not implemented".into()) }
}
}
let store = MyStore;
let req: JmapRequest = serde_json::from_str(r#"{
"using": ["urn:ietf:params:jmap:mail"],
"methodCalls": [["Mailbox/get", {}, "c1"]]
}"#).unwrap();
let resp = dispatch_request(req, "alice@example.com", &store).await;Re-exports§
pub use dispatch::dispatch_method;pub use dispatch::dispatch_request;pub use dispatch::JmapRequest;pub use dispatch::JmapResponse;pub use dispatch::JMAP_CORE_CAP;pub use dispatch::JMAP_MAIL_CAP;pub use dispatch::JMAP_SUBMISSION_CAP;pub use error::JmapMethodError;pub use store::MailStore;pub use store::StoreError;
Modules§
- build
- Build JMAP Email object from a
Message(+ optional parsed body). - dispatch
- Top-level method dispatcher.
- error
- JMAP standard method errors (RFC 8620 §3.6.2).
- fixtures
- In-memory
MailStoreimplementation suitable for tests, examples, and downstream-consumer test harnesses. - flags
- Bitmask <-> JMAP keyword conversions (RFC 8621 §4.1).
- ids
- JMAP id parsers.
- methods
- Per-method JMAP handlers (RFC 8621).
- refs
- Resolve back-references between method calls within one JMAP request (RFC 8620 §3.7 “Back-references”).
- store
- The
MailStoretrait: the abstraction layer between JMAP method handlers and any backing store (PostgreSQL + Maildir, IMAP proxy, etc.). - types
- Minimal JMAP-facing data types.