mail_struct 0.1.21

A minimalist Rust email structure library with optional support for encoding, decoding, and sending via SMTP. / 一个极简的 Rust 邮件结构库,支持可选的编码、解码及 SMTP 发送功能。
Documentation
# mail_struct : Minimalist Email Structure for Rust

`mail_struct` is a lightweight Rust library designed to define a clear and efficient structure for email messages. It provides optional integration with `bitcode` for efficient encoding/decoding and `mail-send` for SMTP transmission with domain-based grouping, making it a versatile choice for email handling in Rust applications.

## Table of Contents

- [Features]#features
- [Usage]#usage
- [Design Philosophy]#design-philosophy
- [Tech Stack]#tech-stack
- [Directory Structure]#directory-structure
- [API Documentation]#api-documentation
- [Historical Context]#historical-context

## Features

- **Core Structure**: Defines `Mail`, `UserMail`, and `HostUserLi` structs to represent email data with type-safe domain grouping.
- **Serialization**: Optional `encode` and `decode` features using `bitcode` for high-performance binary serialization.
- **SMTP Integration**: Optional `send` feature with domain-based recipient grouping for efficient email delivery.
- **Type Safety**: Leverages Rust's type system with custom types like `HostUserLi` to ensure data integrity and prevent misuse.

## Usage

Add `mail_struct` to your `Cargo.toml`:

```toml
[dependencies]
mail_struct = { version = "0.1.18", features = ["send", "encode", "decode"] }
```

### Creating and Grouping Emails by Domain

```rust
use mail_struct::Mail;

#[cfg(feature = "send")]
async fn example() {
    // Mail::new() automatically:
    // - Normalizes and validates email addresses using xmail::norm_user_host
    // - Filters out invalid email addresses
    // - Deduplicates recipients
    // Returns Option<Mail>, None if no valid recipients
    let mail = Mail::new(
        "sender@example.com",
        vec![
            "user1@gmail.com",
            "user2@yahoo.com",
            "user3@gmail.com",
            "user1@gmail.com",  // Duplicate - will be removed
            "invalid-email",     // Invalid - will be filtered out
        ],
        b"Hello, this is a test email!",
    ).unwrap(); // Handle None in production

    // Directly iterate over &mail to get MailMessage grouped by domain
    for msg in &mail {
        println!("Sending to domain: {}", msg.domain);
        
        // 1. Try batch sending to all recipients in this domain
        // MailMessage implements IntoMessage trait, can be passed directly to client.send()
        if let Err(e) = client.send(&msg).await {
            println!("✗ Batch send failed: {}, sending individually", e);
            
            // 2. If batch send fails, use IntoIterator to send individually
            // This uses a custom zero-allocation iterator
            for individual_message in msg {
                match client.send(individual_message).await {
                    Ok(_) => println!("  ✓ Individual send successful"),
                    Err(e) => println!("  ✗ Individual send failed: {}", e),
                }
            }
        } else {
            println!("✓ Batch send successful");
        }
    }
}
```

## Design Philosophy

The library follows a separation of concerns principle. The core `lib.rs` defines the data structures (`Mail`, `UserMail`), keeping the base dependency footprint minimal. Functionalities like serialization and sending are gated behind feature flags (`encode`, `decode`, `send`), allowing users to opt-in only for what they need.

When the `send` feature is active, the `send.rs` module implements `IntoIterator` for `&Mail`, returning `MailMessage` instances grouped by domain. This optimization reduces the number of SMTP connections needed and improves delivery efficiency. Additionally, `MailMessage` also implements `IntoIterator`, providing a zero-overhead fallback mechanism that ensures individual delivery to valid recipients when batch sending fails due to invalid recipient addresses.

## Tech Stack

- **Rust**: Core language.
- **xmail**: Email validation and normalization.
- **bitcode** (Optional): For fast binary encoding and decoding.
- **mail-send** (Optional): For SMTP message construction and sending.

## Directory Structure

```
.
├── Cargo.toml          # Project configuration
├── README.md           # Main documentation
├── readme              # Documentation in specific languages
│   ├── en.md           # English README
│   └── zh.md           # Chinese README
├── src
│   ├── lib.rs          # Core struct definitions and feature gates
│   ├── host_user_li.rs # HostUserLi type for domain-based recipient grouping
│   └── send.rs         # SMTP message logic (feature: send)
└── tests
    └── main.rs         # Integration tests
```

## API Documentation

### `struct Mail`

Represents a basic email message.

- `sender_user: String`: The user part of the sender's email address.
- `sender_host: String`: The domain part of the sender's email address.
- `host_user_li: HostUserLi`: Recipients grouped by domain using a type-safe wrapper around `HashMap<String, HashSet<String>>`.
- `body: Vec<u8>`: The raw body content of the email.

### `struct HostUserLi`

A type-safe wrapper for domain-based recipient grouping that implements `Deref<Target = HashMap<String, HashSet<String>>>`.

#### Methods

##### `add(&mut self, mail: impl AsRef<str>) -> bool`
Adds an email address with automatic validation and normalization using `xmail::norm_user_host`. Returns `true` if the email was valid and added successfully, `false` otherwise. Invalid emails are logged and ignored.

##### `user_li(&self, host: &str) -> Option<&HashSet<String>>`
Returns the set of users for a given host domain, or `None` if the domain doesn't exist.

#### Trait Implementations

##### `Deref<Target = HashMap<String, HashSet<String>>>`
Allows direct read-only access to underlying HashMap methods like `len()`, `is_empty()`, `iter()`, `get()`, etc.

##### `DerefMut<Target = HashMap<String, HashSet<String>>>`
Provides mutable access to the underlying HashMap, enabling direct manipulation using methods like `entry()`, `insert()`, `remove()`, etc. This allows for advanced operations while maintaining type safety.

##### `FromIterator<T: AsRef<str>>`
Enables creation from any iterator of string-like items using `from_iter()`. Each item is validated and added automatically, making it easy to create `HostUserLi` from collections of email addresses.

#### Methods

##### `new(sender: impl AsRef<str>, to_li: impl IntoIterator<Item = impl AsRef<str>>, body: impl Into<Vec<u8>>) -> Option<Self>`

Creates a new `Mail` instance with automatic email processing:
- **Sender Validation**: Uses `xmail::norm_user_host` to validate and normalize the sender address.
- **Recipient Processing**: Uses `HostUserLi::from_iter()` to process all recipients in a functional style.
- **Automatic Validation**: Each recipient is validated and normalized using `xmail::norm_user_host`.
- **Filtering**: Invalid email addresses are automatically filtered out and logged.
- **Deduplication**: Recipients are automatically deduplicated using `HashSet` within each domain.
- **Grouping**: Recipients are organized by domain (host) for efficient processing.
- **Return**: Returns `None` if no valid recipients remain after filtering, or if the sender is invalid.

#### Trait Implementations

##### `IntoIterator` for `&'a Mail` (requires `send` feature)

Implements `IntoIterator` for `&Mail`, returning a custom `MailIter<'a>` iterator.
- **Domain Grouping**: Automatically groups recipients by email domain, with each `MailMessage` containing all recipients for the same domain.
- **Lazy Construction**: The iterator lazily creates `MailMessage` instances during iteration, avoiding pre-allocation of a `Vec`.
- **Efficient Delivery**: Multiple recipients in the same domain can be delivered in a single SMTP transaction.

### `struct UserMail`

A wrapper around `Mail` associating it with a user ID.

- `mail: Mail`: The email content.
- `user_id: u64`: The unique identifier of the user associated with this mail.

### `struct MailMessage<'a>` (requires `send` feature)

Represents an email grouped by recipient domain.

- `sender_user: &'a str`: The user part of the sender's email address.
- `sender_host: &'a str`: The domain part of the sender's email address.
- `domain: &'a str`: The recipient domain name (e.g., "gmail.com").
- `to_li: Vec<Address<'a>>`: List of all recipient addresses for this domain.
- `body: &'a [u8]`: The email body content.

#### Trait Implementations

##### `IntoMessage<'a>`

Implements the `mail_send::smtp::message::IntoMessage` trait, allowing `MailMessage` to be directly converted into an SMTP message containing all recipients. This enables `MailMessage` to be passed directly to `client.send()` for batch sending.

Also implements `IntoMessage` for `&MailMessage`, allowing message creation without consuming ownership.

##### `IntoIterator`

Implements the `IntoIterator` trait, returning a custom `MailMessageIter` iterator.
- **Zero Allocation**: The iterator lazily constructs `Message` instances during iteration, avoiding the overhead of creating an intermediate `Vec`.
- **Fallback Strategy**: Converts `MailMessage` into multiple individual `Message` instances (each containing one recipient). This is useful when batch sending fails, allowing for individual delivery to ensure valid recipients receive the email.

## Historical Context

**RFC 822 and the Separation of Envelope and Content**

The design of email systems dates back to the early 1980s with the publication of **RFC 822** (Standard for the Format of ARPA Internet Text Messages) and **RFC 821** (Simple Mail Transfer Protocol). A key architectural decision was the separation of the "envelope" (handled by SMTP for routing) from the "content" (the message headers and body defined by RFC 822).

`mail_struct` honors this tradition by focusing on the *structure* of the message (the content), while delegating the *transport* (the envelope and transmission) to specialized libraries like `mail-send`. This modular approach mirrors the original design philosophy of the internet's most enduring communication protocol, ensuring flexibility and maintainability.