5 unstable releases

0.3.2 Dec 29, 2023
0.3.1 Dec 28, 2023
0.3.0 Jun 6, 2023
0.2.0 Oct 28, 2022
0.1.0 Sep 9, 2022

#106 in Email

Download history 42/week @ 2023-12-24 13/week @ 2023-12-31 34/week @ 2024-01-07 57/week @ 2024-01-14 23/week @ 2024-01-21 12/week @ 2024-01-28 1/week @ 2024-02-04 26/week @ 2024-02-11 20/week @ 2024-02-18 45/week @ 2024-02-25 14/week @ 2024-03-03 23/week @ 2024-03-10 8/week @ 2024-03-17 1/week @ 2024-03-24 48/week @ 2024-03-31 27/week @ 2024-04-07

87 downloads per month

Apache-2.0 OR MIT

400KB
11K SLoC

jmap-client

crates.io build docs.rs crates.io

jmap-client is a JSON Meta Application Protocol (JMAP) library written in Rust. The library is a full implementation of the JMAP RFCs including:

Features:

  • Async and blocking support (use the cargo feature blocking to enable blocking).
  • WebSocket async streams (use the cargo feature websockets to enable JMAP over WebSocket).
  • EventSource async streams.
  • Helper functions to reduce boilerplate code and quickly build JMAP requests.
  • Fast parsing and encoding of JMAP requests.

Usage Example

// Connect to the JMAP server using Basic authentication.
// (just for demonstration purposes, Bearer tokens should be used instead)
let client = Client::new()
    .credentials(("john@example.org", "secret"))
    .connect("https://jmap.example.org")
    .await
    .unwrap();

// Create a mailbox.
let mailbox_id = client
    .mailbox_create("My Mailbox", None::<String>, Role::None)
    .await
    .unwrap()
    .take_id();

// Import a message into the mailbox.
client
    .email_import(
        b"From: john@example.org\nSubject: test\n\n test".to_vec(),
        [&mailbox_id],
        ["$draft"].into(),
        None,
    )
    .await
    .unwrap();

// Obtain all e-mail ids matching a filter.
let email_id = client
    .email_query(
        Filter::and([
            email::query::Filter::subject("test"),
            email::query::Filter::in_mailbox(&mailbox_id),
            email::query::Filter::has_keyword("$draft"),
        ])
        .into(),
        [email::query::Comparator::from()].into(),
    )
    .await
    .unwrap()
    .take_ids()
    .pop()
    .unwrap();

// Fetch an e-mail message.
let email = client
    .email_get(
        &email_id,
        [Property::Subject, Property::Preview, Property::Keywords].into(),
    )
    .await
    .unwrap()
    .unwrap();
assert_eq!(email.preview().unwrap(), "test");
assert_eq!(email.subject().unwrap(), "test");
assert_eq!(email.keywords(), ["$draft"]);

// Fetch only the updated properties of all mailboxes that changed
// since a state.
let mut request = client.build();
let changes_request = request.changes_mailbox("n").max_changes(0);
let properties_ref = changes_request.updated_properties_reference();
let updated_ref = changes_request.updated_reference();
request
    .get_mailbox()
    .ids_ref(updated_ref)
    .properties_ref(properties_ref);
for mailbox in request
    .send()
    .await
    .unwrap()
    .unwrap_method_responses()
    .pop()
    .unwrap()
    .unwrap_get_mailbox()
    .unwrap()
    .take_list()
{
    println!("Changed mailbox: {:#?}", mailbox);
}

// Delete the mailbox including any messages
client.mailbox_destroy(&mailbox_id, true).await.unwrap();

// Open an EventSource connection with the JMAP server.
let mut stream = client
    .event_source(
        [
            TypeState::Email,
            TypeState::EmailDelivery,
            TypeState::Mailbox,
            TypeState::EmailSubmission,
            TypeState::Identity,
        ]
        .into(),
        false,
        60.into(),
        None,
    )
    .await
    .unwrap();

// Consume events received over EventSource.
while let Some(event) = stream.next().await {
    let changes = event.unwrap();
    println!("-> Change id: {:?}", changes.id());
    for account_id in changes.changed_accounts() {
        println!(" Account {} has changes:", account_id);
        if let Some(account_changes) = changes.changes(account_id) {
            for (type_state, state_id) in account_changes {
                println!("   Type {:?} has a new state {}.", type_state, state_id);
            }
        }
    }
}

More examples available under the examples directory.

Testing

To run the testsuite:

 $ cargo test --all-features

Conformed RFCs

License

Licensed under either of

at your option.

Copyright (C) 2022, Stalwart Labs Ltd.

Dependencies

~6–22MB
~335K SLoC