Skip to content

pimalaya/io-gmail

Repository files navigation

I/O Gmail Documentation Matrix Mastodon

Gmail REST API client library, written in Rust.

https://developers.google.com/workspace/gmail/api/reference/rest

Table of contents

Usage

I/O Gmail can be consumed three ways, depending on how much of the I/O stack you want to own. Each mode is gated by cargo features.

Tip

I/O Gmail is written in Rust and uses cargo features to gate the client layers. The default feature set is declared in Cargo.toml or on docs.rs.

Full client

If you want a ready-to-use, standard, blocking client with TCP connection and TLS negociation managed for you:

[dependencies]
io-gmail = "0.0.1" # rustls-ring is enabled by default
use io_gmail::v1::client::GmailClientStd;

let mut client = GmailClientStd::connect("token", Default::default()).unwrap();

let out = client.profile_get().unwrap();
println!("Email address: {}", out.response.email_address);

let out = client.labels_list().unwrap();
for label in &out.response.labels {
    println!("{}: {}", label.id, label.name);
}

Light client

If you still want a standard, blocking client but you want to manage TCP and TLS on your own:

[dependencies]
io-gmail = { version = "0.0.1", default-features = false, features = ["client"] }
rustls = "0.23"
rustls-platform-verifier = "0.7"
use std::{net::TcpStream, sync::Arc};

use io_gmail::v1::client::GmailClientStd;
use rustls::{ClientConfig, ClientConnection, StreamOwned};
use rustls_platform_verifier::ConfigVerifierExt;

// TLS config
let config = ClientConfig::with_platform_verifier().unwrap();
let server_name = "gmail.googleapis.com".try_into().unwrap();
let conn = ClientConnection::new(Arc::new(config), server_name).unwrap();
let tcp = TcpStream::connect(("gmail.googleapis.com", 443)).unwrap();
let stream = StreamOwned::new(conn, tcp);

// Standard, blocking client
let mut client = GmailClientStd::new(stream, "token", Default::default());

let out = client.profile_get().unwrap();
println!("Email address: {}", out.response.email_address);

let out = client.labels_list().unwrap();
for label in &out.response.labels {
    println!("{}: {}", label.id, label.name);
}

Coroutines

Otherwise you can build your own client using I/O-free coroutines directly:

[dependencies]
io-gmail = { version = "0.0.1", default-features = false }
rustls = "0.23"
rustls-platform-verifier = "0.7"
tokio = { version = "1", features = ["full"] }
tokio-rustls = "0.26"
use std::sync::Arc;

use io_gmail::{coroutine::*, v1::rest::users::get_profile::GmailProfileGet};
use io_http::rfc6750::bearer::HttpAuthBearer;
use rustls::ClientConfig;
use rustls_platform_verifier::ConfigVerifierExt;
use tokio::{
    io::{AsyncReadExt, AsyncWriteExt},
    net::TcpStream,
};
use tokio_rustls::TlsConnector;

#[tokio::main]
async fn main() {
    // Async TLS connection
    let config = ClientConfig::with_platform_verifier().unwrap();
    let connector = TlsConnector::from(Arc::new(config));
    let server_name = "gmail.googleapis.com".try_into().unwrap();
    let tcp = TcpStream::connect(("gmail.googleapis.com", 443)).await.unwrap();
    let mut stream = connector.connect(server_name, tcp).await.unwrap();

    // Run the I/O-free coroutine against the async stream
    let auth = HttpAuthBearer::new("token");
    let mut coroutine = GmailProfileGet::new(&auth, "me").unwrap();
    let mut arg: Option<&[u8]> = None;
    let mut buf = [0u8; 8192];
    let mut read_buf = Vec::<u8>::new();

    let out = loop {
        match coroutine.resume(arg.take()) {
            GmailCoroutineState::Complete(Ok(out)) => break out,
            GmailCoroutineState::Yielded(GmailYield::WantsRead) => {
                let n = stream.read(&mut buf).await.unwrap();
                read_buf.clear();
                read_buf.extend_from_slice(&buf[..n]);
                arg = Some(&read_buf);
            }
            GmailCoroutineState::Yielded(GmailYield::WantsWrite(bytes)) => {
                stream.write_all(&bytes).await.unwrap();
            }
            GmailCoroutineState::Complete(Err(err)) => panic!("{err}"),
        }
    };

    println!("Email address: {}", out.response.email_address);
}

Important

For such advanced usage, it is preferable to read the architecture guide.

Examples

Have a look at real-world projects built on top of this library:

AI disclosure

This project is developed with AI assistance. This section documents how, so users and downstream packagers can make informed decisions.

  • Tools: Claude Code (Anthropic), Opus 4.8, invoked locally with a persistent project-scoped memory and a small set of repo-specific rules.
  • Used for: Refactors, mechanical multi-file edits, boilerplate (feature gates, error enums, derive macros, trait impls), test scaffolding, doc polish, exploratory design conversations.
  • Not used for: Engineering, critical code, git manipulation (commit, merge, rebase…), real-world tests.
  • Verification: Every AI-assisted change is read, compiled, tested, and formatted before commit (nix develop --command cargo check / cargo test / cargo fmt). Behavioural correctness is verified against the Gmail REST API reference, not assumed from the model output. Tests are never adjusted to fit AI-generated code; the code is adjusted to fit correct behaviour.
  • Limitations: AI models occasionally produce code that compiles and passes tests but is subtly wrong: off-by-one errors, missed edge cases, plausible but nonexistent APIs, stale spec references. The verification workflow catches most of this; it does not catch all of it. Bug reports are welcome and taken seriously.
  • Last reviewed: 16/06/2026

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