plunk-rs 0.1.2

Async Rust client for the Plunk transactional email API
Documentation
<div align="center">

# plunk-rs

**An async Rust client for [Plunk](https://www.useplunk.com/) transactional email.**

*Send HTML, templates, and template variables (the `data` object) with validation, typed errors, and a small dependency footprint (reqwest + rustls).*

[![License: MIT](https://img.shields.io/badge/License-MIT-5c6bc0.svg)](./LICENSE)
[![crates.io](https://img.shields.io/crates/v/plunk-rs.svg)](https://crates.io/crates/plunk-rs)
[![docs.rs](https://img.shields.io/docsrs/plunk-rs)](https://docs.rs/plunk-rs)

[Repository](https://git.coldspire.com/Antfroze/plunk-rs) · [Plunk API](https://docs.useplunk.com)

</div>

---

## Why this crate

| | |
| :--- | :--- |
| **Focused** | Covers `POST /v1/send`—HTML and template sends, with optional `from`, `reply`, headers, and `data` for template variables. |
| **Validates early** | `Email` and `EmailAddress` are built with checks so bad requests fail before the wire. |
| **Clean boundary** | Wire DTOs stay internal; your code uses `Email`, `SendResponse`, and `Error`. |
| **TLS** | [reqwest]https://crates.io/crates/reqwest with **rustls**—no OpenSSL at build time. |

## Installation

Add to `Cargo.toml`:

```toml
[dependencies]
plunk-rs = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```

Or:

```text
cargo add plunk-rs
cargo add tokio --features rt-multi-thread,macros
```

## Quick start

```rust
use plunk_rs::{Client, Email, EmailAddress};

#[tokio::main]
async fn main() -> Result<(), plunk_rs::Error> {
    let client = Client::new("sk_your_plunk_api_key")?;

    let email = Email::html("user@example.com", "Welcome", "<h1>Hello</h1>")?
        .from(EmailAddress::named("My app", "hello@yourdomain.com")?)?
        .reply_to("support@yourdomain.com")?;

    let response = client.send(&email).await?;

    for delivery in response.deliveries() {
        println!("{} → {}", delivery.contact().email(), delivery.id());
    }
    Ok(())
}
```

**Template send** (with data for the template):

```rust
use plunk_rs::{Client, Email};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), plunk_rs::Error> {
    let client = Client::new("sk_your_plunk_api_key")?;

    let email = Email::template("user@example.com", "550e8400-e29b-41d4-a716-446655440000")?
        .with_data(json!({ "first_name": "Ada" }))?;

    client.send(&email).await?;
    Ok(())
}
```

## Self-hosted or custom base URL

```rust
let client = Client::builder("sk_xxx")
    .base_url("https://plunk.example.com/api")?
    .build()?;
```

`base_url` is normalized: if the path does not end with `/`, one is added so `v1/send` joins correctly.

## API surface

- **`Client` / `ClientBuilder`** — Bearer API key, optional `reqwest::Client` injection.
- **`Email`**, **`EmailAddress`**, **`Recipients`** — Construction helpers for HTML vs template, headers, and JSON `data` (must serialize to a JSON object for templates).
- **`SendResponse`**, **`Delivery`**, **`Contact`** — Parsed success payload from the send response.
- **`Error`**, **`ApiError`**, **`Result<T>`** — Transport, validation, and Plunk error bodies (with status and raw body preserved).

## Requirements

Rust **2024** edition (use a current stable toolchain that supports it). Async code assumes a runtime such as **Tokio**.

## Docs

- [**docs.rs** — plunk-rs]https://docs.rs/plunk-rs
- [Plunk documentation]https://docs.useplunk.com

## License

[MIT](LICENSE)

---

<div align="center">

Made for sending mail through [Plunk](https://www.useplunk.com/) without the ceremony.

</div>