yamime

Crates.ioyamime
lib.rsyamime
version0.1.0
created_at2026-01-10 08:45:32.813783+00
updated_at2026-01-10 08:45:32.813783+00
descriptionComplete Rust port of Go's mime package with async-first design
homepage
repositoryhttps://github.com/async-php/yamime
max_upload_size
id2033863
size172,794
changzee (changzee)

documentation

README

yamime

Crates.io Documentation License Build Status

Complete Rust port of Go's mime package with async-first design

A comprehensive MIME handling library for Rust, providing full support for MIME type detection, media type parsing, multipart messages, quoted-printable encoding, and RFC 2047 encoded-words. Built with async/await and tokio for modern Rust applications.

Features

  • ๐ŸŽฏ MIME Type Detection - File extension to MIME type mapping with 1000+ built-in types
  • ๐Ÿ“ Media Type Parsing - RFC 2045/2616/2231 compliant media type handling
  • ๐Ÿ“ฎ Multipart Messages - Full multipart/form-data and multipart/mixed support
  • ๐Ÿ”ค Encoded Words - RFC 2047 encoded-word encoding/decoding for email headers
  • โœ‰๏ธ Quoted-Printable - RFC 2045 quoted-printable encoding/decoding
  • โšก Async First - Built on tokio for high-performance async I/O
  • ๐Ÿฆ€ Pure Rust - No unsafe code, fully type-safe
  • ๐Ÿงช Well Tested - 121+ tests with 73.78% code coverage

Installation

Add this to your Cargo.toml:

[dependencies]
yamime = "0.1.0"
tokio = { version = "1", features = ["full"] }

Quick Start

MIME Type Detection

use yamime::type_by_extension;

// Get MIME type by file extension
let mime_type = type_by_extension(".html");
assert_eq!(mime_type, Some("text/html; charset=utf-8".to_string()));

let mime_type = type_by_extension(".jpg");
assert_eq!(mime_type, Some("image/jpeg".to_string()));

Media Type Parsing

use yamime::parse_media_type;

let (media_type, params) = parse_media_type("text/html; charset=utf-8").unwrap();
assert_eq!(media_type, "text/html");
assert_eq!(params.get("charset"), Some(&"utf-8".to_string()));

Multipart Form Data

use yamime::multipart::Writer;
use tokio::io::AsyncWriteExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut output = Vec::new();
    let mut writer = Writer::new(&mut output);

    // Add a text field
    writer.write_field("username", "john_doe").await?;

    // Add a file
    let mut file_writer = writer.create_form_file("avatar", "photo.jpg").await?;
    file_writer.write_all(b"image data here").await?;

    writer.close().await?;

    println!("Multipart data created: {} bytes", output.len());
    Ok(())
}

Reading Multipart Data

use yamime::multipart::Reader;
use tokio::io::AsyncReadExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = b"--boundary\r\n\
Content-Disposition: form-data; name=\"field\"\r\n\
\r\n\
value\r\n\
--boundary--\r\n";

    let mut reader = Reader::new(&data[..], "boundary");

    while let Some(mut part) = reader.next_part().await? {
        println!("Field name: {:?}", part.form_name());

        let mut content = String::new();
        part.read_to_string(&mut content).await?;
        println!("Content: {}", content);
    }

    Ok(())
}

Quoted-Printable Encoding

use yamime::quotedprintable::Writer;
use tokio::io::AsyncWriteExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut output = Vec::new();
    let mut writer = Writer::new(&mut output);

    writer.write_all(b"Hello, World! Special chars: =").await?;
    writer.close().await?;

    println!("Encoded: {}", String::from_utf8_lossy(&output));
    // Output: "Hello, World! Special chars: =3D"

    Ok(())
}

Encoded Words (Email Headers)

use yamime::{WordEncoder, WordDecoder};

// Encoding
let encoder = WordEncoder::QEncoding;
let encoded = encoder.encode("UTF-8", "Hello, ไธ–็•Œ!");
println!("Encoded: {}", encoded);
// Output: =?UTF-8?q?Hello,_=E4=B8=96=E7=95=8C!?=

// Decoding
let decoder = WordDecoder::new();
let decoded = decoder.decode(&encoded).unwrap();
assert_eq!(decoded, "Hello, ไธ–็•Œ!");

API Overview

Core Modules

  • mime_type - MIME type detection and extension mapping
  • media_type - Media type parsing and formatting (RFC 2045/2616/2231)
  • multipart - Multipart message handling (RFC 2046/2388)
    • Reader - Parse multipart messages
    • Writer - Create multipart messages
    • Form - Multipart form data support
  • quotedprintable - Quoted-printable encoding (RFC 2045)
    • Reader - Decode quoted-printable
    • Writer - Encode quoted-printable
  • encoded_word - RFC 2047 encoded-word support
    • WordEncoder - Encode headers
    • WordDecoder - Decode headers
  • error - Error types and result definitions

Platform Support

The library includes platform-specific MIME type loading:

  • Unix/Linux/macOS - Loads from /etc/mime.types and other standard locations
  • Windows - Reads from Windows Registry

Performance

The library includes comprehensive benchmarks using Criterion:

cargo bench

Example benchmark results:

  • Media type parsing: ~125 ns per operation
  • Quoted-printable encoding (1KB): ~45 ยตs (22 MiB/s)
  • Multipart writing (5 parts): ~150 ยตs

Testing

Run Tests

# All tests (121+ tests)
cargo test

# Unit tests only
cargo test --lib

# Integration tests
cargo test --test integration_tests

# With coverage
cargo tarpaulin --out Html

Test Coverage

Current test coverage: 73.78% (695/942 lines)

  • โœ… error.rs: 100%
  • โœ… mime_type.rs: 97%
  • โœ… encoded_word.rs: ~88%
  • โœ… quotedprintable/writer.rs: ~85%
  • โœ… multipart/writer.rs: 81%

See TESTING_GUIDE.md for detailed testing documentation.

Fuzzing

The project includes fuzz tests using cargo-fuzz:

# Install cargo-fuzz
cargo install cargo-fuzz

# Run fuzzing (examples)
cargo fuzz run fuzz_parse_media_type -- -max_total_time=60
cargo fuzz run fuzz_multipart_reader -- -max_total_time=60
cargo fuzz run fuzz_encoded_word -- -max_total_time=60
cargo fuzz run fuzz_quoted_printable -- -max_total_time=60

Examples

See the examples/ directory for more complete examples:

  • basic_mime_type.rs - MIME type detection
  • multipart_form.rs - Creating multipart forms
  • email_headers.rs - Encoding email headers
  • file_upload.rs - Handling file uploads

Run examples:

cargo run --example basic_mime_type

Comparison with Other Libraries

Feature yamime mime mailparse
MIME type detection โœ… โŒ โŒ
Media type parsing โœ… โœ… โœ…
Multipart messages โœ… โŒ โœ…
Quoted-printable โœ… โŒ โœ…
Encoded words (RFC 2047) โœ… โŒ โœ…
Async/await โœ… โŒ โŒ
Form data โœ… โŒ โŒ
Writing support โœ… โŒ โŒ

Requirements

  • Rust: 1.70 or later
  • Tokio: 1.35 or later (async runtime)

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Setup

# Clone the repository
git clone https://github.com/async-php/yamime.git
cd yamime

# Run tests
cargo test

# Run benchmarks
cargo bench

# Check formatting
cargo fmt --check

# Run clippy
cargo clippy -- -D warnings

Guidelines

  1. Write tests for new features
  2. Maintain or improve code coverage
  3. Follow Rust naming conventions
  4. Add documentation for public APIs
  5. Run cargo fmt before committing

License

This project is dual-licensed under:

at your option.

Acknowledgments

  • Inspired by Go's mime package
  • Built with tokio async runtime
  • Tested with Criterion benchmark framework
  • Fuzzed with cargo-fuzz

Resources

RFCs Implemented

  • RFC 2045 - MIME Part One: Format of Internet Message Bodies
  • RFC 2046 - MIME Part Two: Media Types
  • RFC 2047 - MIME Part Three: Message Header Extensions
  • RFC 2231 - MIME Parameter Value and Encoded Word Extensions
  • RFC 2388 - Returning Values from Forms: multipart/form-data
  • RFC 2616 - HTTP/1.1 (Media Type handling)

Made with โค๏ธ in Rust

Commit count: 15

cargo fmt