122 lines
3.5 KiB
Markdown
122 lines
3.5 KiB
Markdown
<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)
|
|
[](https://crates.io/crates/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>
|