<div align="center">
<h1>emailaddr</h1>
</div>
<div align="center">
Type-safe, zero-copy, validated email addresses with generic storage, IDNA domain support, and `no_std`/`no-alloc` compatibility.
[<img alt="github" src="https://img.shields.io/badge/github-al8n/emailaddr-8da0cb?style=for-the-badge&logo=Github" height="22">][Github-url]
<img alt="LoC" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fal8n%2F327b2a8aef9003246e45c6e47fe63937%2Fraw%2Femailaddr" height="22">
[<img alt="Build" src="https://img.shields.io/github/actions/workflow/status/al8n/emailaddr/ci.yml?logo=Github-Actions&style=for-the-badge" height="22">][CI-url]
[<img alt="codecov" src="https://img.shields.io/codecov/c/gh/al8n/emailaddr?style=for-the-badge&token=6R3QFWRWHL&logo=codecov" height="22">][codecov-url]
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-emailaddr-66c2a5?style=for-the-badge&labelColor=555555&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">][doc-url]
[<img alt="crates.io" src="https://img.shields.io/crates/v/emailaddr?style=for-the-badge&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgdmlld0JveD0iMCAwIDUxMiA1MTIiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPGc+DQoJPGc+DQoJCTxwYXRoIGQ9Ik0yNTYsMEwzMS41MjgsMTEyLjIzNnYyODcuNTI4TDI1Niw1MTJsMjI0LjQ3Mi0xMTIuMjM2VjExMi4yMzZMMjU2LDB6IE0yMzQuMjc3LDQ1Mi41NjRMNzQuOTc0LDM3Mi45MTNWMTYwLjgxDQoJCQlsMTU5LjMwMyw3OS42NTFWNDUyLjU2NHogTTEwMS44MjYsMTI1LjY2MkwyNTYsNDguNTc2bDE1NC4xNzQsNzcuMDg3TDI1NiwyMDIuNzQ5TDEwMS44MjYsMTI1LjY2MnogTTQzNy4wMjYsMzcyLjkxMw0KCQkJbC0xNTkuMzAzLDc5LjY1MVYyNDAuNDYxbDE1OS4zMDMtNzkuNjUxVjM3Mi45MTN6IiBmaWxsPSIjRkZGIi8+DQoJPC9nPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPC9zdmc+DQo=" height="22">][crates-url]
[<img alt="crates.io" src="https://img.shields.io/crates/d/emailaddr?color=critical&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjQ1MTE3MzMyOTU5IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjM0MjEiIGRhdGEtc3BtLWFuY2hvci1pZD0iYTMxM3guNzc4MTA2OS4wLmkzIiB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48ZGVmcz48c3R5bGUgdHlwZT0idGV4dC9jc3MiPjwvc3R5bGU+PC9kZWZzPjxwYXRoIGQ9Ik00NjkuMzEyIDU3MC4yNHYtMjU2aDg1LjM3NnYyNTZoMTI4TDUxMiA3NTYuMjg4IDM0MS4zMTIgNTcwLjI0aDEyOHpNMTAyNCA2NDAuMTI4QzEwMjQgNzgyLjkxMiA5MTkuODcyIDg5NiA3ODcuNjQ4IDg5NmgtNTEyQzEyMy45MDQgODk2IDAgNzYxLjYgMCA1OTcuNTA0IDAgNDUxLjk2OCA5NC42NTYgMzMxLjUyIDIyNi40MzIgMzAyLjk3NiAyODQuMTYgMTk1LjQ1NiAzOTEuODA4IDEyOCA1MTIgMTI4YzE1Mi4zMiAwIDI4Mi4xMTIgMTA4LjQxNiAzMjMuMzkyIDI2MS4xMkM5NDEuODg4IDQxMy40NCAxMDI0IDUxOS4wNCAxMDI0IDY0MC4xOTJ6IG0tMjU5LjItMjA1LjMxMmMtMjQuNDQ4LTEyOS4wMjQtMTI4Ljg5Ni0yMjIuNzItMjUyLjgtMjIyLjcyLTk3LjI4IDAtMTgzLjA0IDU3LjM0NC0yMjQuNjQgMTQ3LjQ1NmwtOS4yOCAyMC4yMjQtMjAuOTI4IDIuOTQ0Yy0xMDMuMzYgMTQuNC0xNzguMzY4IDEwNC4zMi0xNzguMzY4IDIxNC43MiAwIDExNy45NTIgODguODMyIDIxNC40IDE5Ni45MjggMjE0LjRoNTEyYzg4LjMyIDAgMTU3LjUwNC03NS4xMzYgMTU3LjUwNC0xNzEuNzEyIDAtODguMDY0LTY1LjkyLTE2NC45MjgtMTQ0Ljk2LTE3MS43NzZsLTI5LjUwNC0yLjU2LTUuODg4LTMwLjk3NnoiIGZpbGw9IiNmZmZmZmYiIHAtaWQ9IjM0MjIiIGRhdGEtc3BtLWFuY2hvci1pZD0iYTMxM3guNzc4MTA2OS4wLmkwIiBjbGFzcz0iIj48L3BhdGg+PC9zdmc+&style=for-the-badge" height="22">][crates-url]
<img alt="license" src="https://img.shields.io/badge/License-Apache%202.0/MIT-blue.svg?style=for-the-badge&fontColor=white&logoColor=f5c076&logo=data:image/svg+xml;base64,PCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KDTwhLS0gVXBsb2FkZWQgdG86IFNWRyBSZXBvLCB3d3cuc3ZncmVwby5jb20sIFRyYW5zZm9ybWVkIGJ5OiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4KPHN2ZyBmaWxsPSIjZmZmZmZmIiBoZWlnaHQ9IjgwMHB4IiB3aWR0aD0iODAwcHgiIHZlcnNpb249IjEuMSIgaWQ9IkNhcGFfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmlld0JveD0iMCAwIDI3Ni43MTUgMjc2LjcxNSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgc3Ryb2tlPSIjZmZmZmZmIj4KDTxnIGlkPSJTVkdSZXBvX2JnQ2FycmllciIgc3Ryb2tlLXdpZHRoPSIwIi8+Cg08ZyBpZD0iU1ZHUmVwb190cmFjZXJDYXJyaWVyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KDTxnIGlkPSJTVkdSZXBvX2ljb25DYXJyaWVyIj4gPGc+IDxwYXRoIGQ9Ik0xMzguMzU3LDBDNjIuMDY2LDAsMCw2Mi4wNjYsMCwxMzguMzU3czYyLjA2NiwxMzguMzU3LDEzOC4zNTcsMTM4LjM1N3MxMzguMzU3LTYyLjA2NiwxMzguMzU3LTEzOC4zNTcgUzIxNC42NDgsMCwxMzguMzU3LDB6IE0xMzguMzU3LDI1OC43MTVDNzEuOTkyLDI1OC43MTUsMTgsMjA0LjcyMywxOCwxMzguMzU3UzcxLjk5MiwxOCwxMzguMzU3LDE4IHMxMjAuMzU3LDUzLjk5MiwxMjAuMzU3LDEyMC4zNTdTMjA0LjcyMywyNTguNzE1LDEzOC4zNTcsMjU4LjcxNXoiLz4gPHBhdGggZD0iTTE5NC43OTgsMTYwLjkwM2MtNC4xODgtMi42NzctOS43NTMtMS40NTQtMTIuNDMyLDIuNzMyYy04LjY5NCwxMy41OTMtMjMuNTAzLDIxLjcwOC0zOS42MTQsMjEuNzA4IGMtMjUuOTA4LDAtNDYuOTg1LTIxLjA3OC00Ni45ODUtNDYuOTg2czIxLjA3Ny00Ni45ODYsNDYuOTg1LTQ2Ljk4NmMxNS42MzMsMCwzMC4yLDcuNzQ3LDM4Ljk2OCwyMC43MjMgYzIuNzgyLDQuMTE3LDguMzc1LDUuMjAxLDEyLjQ5NiwyLjQxOGM0LjExOC0yLjc4Miw1LjIwMS04LjM3NywyLjQxOC0xMi40OTZjLTEyLjExOC0xNy45MzctMzIuMjYyLTI4LjY0NS01My44ODItMjguNjQ1IGMtMzUuODMzLDAtNjQuOTg1LDI5LjE1Mi02NC45ODUsNjQuOTg2czI5LjE1Miw2NC45ODYsNjQuOTg1LDY0Ljk4NmMyMi4yODEsMCw0Mi43NTktMTEuMjE4LDU0Ljc3OC0zMC4wMDkgQzIwMC4yMDgsMTY5LjE0NywxOTguOTg1LDE2My41ODIsMTk0Ljc5OCwxNjAuOTAzeiIvPiA8L2c+IDwvZz4KDTwvc3ZnPg==" height="22">
[<img alt="Discord" src="https://img.shields.io/discord/835936528140206122?style=for-the-badge&logo=discord&logoColor=white&label=Discord&color=7289da" height="22">][discord]
</div>
## Overview
`emailaddr` provides validated email address types with a storage model similar
to [`hostaddr`](https://github.com/al8n/hostaddr):
- `EmailAddr<S = str>` stores a validated full address using caller-chosen
storage.
- `LocalPart<S = str>` validates dot-atom and quoted-string local-parts.
- `DomainPart<S = str>` validates RFC 5321 domain-parts and bracketed address
literals.
- `Buffer` stores a full validated address on the stack for `no_std` /
no-allocation use.
With the default `std` feature, SMTPUTF8 local-parts are accepted and Unicode
domain-parts are normalized to IDNA/punycode. The `verify_ascii_*` helpers
remain strict ASCII validators.
## Installation
```toml
[dependencies]
emailaddr = "0.1"
```
## Quick Start
```rust,ignore
use emailaddr::EmailAddr;
let addr: EmailAddr<String> = "user.name@example.com".parse().unwrap();
assert_eq!(addr.local_part().as_inner(), &"user.name");
assert_eq!(addr.domain_part().as_inner(), &"example.com");
let quoted = EmailAddr::try_from_ascii_str("\"user name\"@example.com").unwrap();
assert_eq!(quoted.local_part().as_inner(), &"\"user name\"");
let idn: EmailAddr<String> = "user@测试.中国".parse().unwrap();
assert_eq!(idn.as_str(), "user@xn--0zwm56d.xn--fiqs8s");
let smtp_utf8: EmailAddr<String> = "用户@example.com".parse().unwrap();
assert_eq!(smtp_utf8.local_part().as_inner(), &"用户");
```
## Borrowed DSTs
```rust,ignore
use emailaddr::{DomainPart, EmailAddr, LocalPart};
let addr: &EmailAddr<str> = EmailAddr::try_from_ascii_str("user@example.com").unwrap();
assert_eq!(addr.as_inner(), "user@example.com");
assert_eq!(addr.local_part_ref().as_inner(), "user");
assert_eq!(addr.domain_part_ref().as_inner(), "example.com");
let bytes: &EmailAddr<[u8]> = addr.as_bytes_addr();
assert_eq!(bytes.as_str_addr().as_inner(), "user@example.com");
let local: &LocalPart<str> = LocalPart::try_from_ascii_str("user").unwrap();
let domain: &DomainPart<str> = DomainPart::try_from_ascii_str("example.com").unwrap();
assert_eq!(local.as_bytes().as_inner(), b"user");
assert_eq!(domain.as_bytes().as_inner(), b"example.com");
```
## Storage
```rust,ignore
use emailaddr::{Buffer, EmailAddr};
use std::sync::Arc;
let owned: EmailAddr<String> = "user@example.com".parse().unwrap();
let shared: EmailAddr<Arc<str>> = EmailAddr::try_from("user@example.com").unwrap();
let bytes: EmailAddr<Vec<u8>> = EmailAddr::try_from(b"user@example.com".as_slice()).unwrap();
let stack: EmailAddr<Buffer> = EmailAddr::try_from("user@example.com").unwrap();
assert_eq!(owned.as_str(), shared.as_str());
assert_eq!(bytes.as_bytes(), stack.as_bytes());
```
## Validation Helpers
```rust,ignore
use emailaddr::{
verify_ascii_domain_part,
verify_ascii_email_addr,
verify_ascii_local_part,
verify_email_addr,
};
assert!(verify_ascii_email_addr(b"user@example.com").is_ok());
assert!(verify_email_addr("user@测试.中国".as_bytes()).is_ok());
assert!(verify_email_addr("用户@example.com".as_bytes()).is_ok());
assert!(verify_ascii_local_part(b"user.name").is_ok());
assert!(verify_ascii_domain_part(b"[IPv6:::1]").is_ok());
assert!(verify_ascii_email_addr(b"user..name@example.com").is_err());
assert!(verify_ascii_email_addr(b"user@example_com").is_err());
assert!(verify_ascii_email_addr(b"user@example.com.").is_err());
```
## Parsing Options
Default parsing stays RFC-compatible. [`Options`] adds application policy and
explicit non-standard relaxations without changing the default constructors.
`parse_with_options` is available on `EmailAddr<S, Relax>`, so relaxed parsing
is visible in the type while ordinary `EmailAddr<S>` remains strict.
```rust,ignore
use emailaddr::{
Buffer,
DomainOptions,
DomainUnicodePolicy,
EmailAddr,
Limits,
LocalOptions,
Options,
Relax,
SmtpUtf8Policy,
};
let require_tld = Options::new()
.with_domain(DomainOptions::new().with_required_tld());
assert!(EmailAddr::<Buffer, Relax>::parse_with_options("ted.backer@gmail", require_tld).is_err());
let long_local = Options::new()
.with_limits(Limits::new().with_max_local_part_len(128));
assert!(EmailAddr::<Buffer, Relax>::parse_with_options(
"reply+2a907e&3uofr1&&99cd5c22c2ca5b23655799316a8d8eb2dd83c3c487612cb9b9a00bf13f13afe2@mg1.substack.com",
long_local,
).is_ok());
let ascii_local = Options::new()
.with_local(LocalOptions::new().with_smtp_utf8(SmtpUtf8Policy::Forbid));
assert!(EmailAddr::<Buffer, Relax>::parse_with_options("用户@example.com", ascii_local).is_err());
let no_literals = Options::new()
.with_domain(DomainOptions::new().without_domain_literals());
assert!(EmailAddr::<Buffer, Relax>::parse_with_options("user@[127.0.0.1]", no_literals).is_err());
let raw_domain = Options::new()
.with_domain(DomainOptions::new().with_unicode(DomainUnicodePolicy::NonStandardUtf8));
let addr = EmailAddr::<Buffer, Relax>::parse_with_options("👋@💌.kz", raw_domain).unwrap();
assert_eq!(addr.as_str(), "👋@💌.kz");
```
`DomainUnicodePolicy::NonStandardUtf8` preserves raw UTF-8 DNS labels as
application data. It is not SMTP/DNS-compatible IDNA normalization.
The `DomainUnicodePolicy::as_str()` values, CLI values, and human-readable
serde values for this policy are `ascii`, `idna`, and `raw`.
Human-readable serde formats such as JSON, YAML, and TOML serialize option
enums with those same string values, so configuration files can use fields like:
```toml
[domain]
unicode = "raw"
literals = "forbid"
minimum_dns_labels = 2
```
## Feature Flags
| Feature | Description |
|---------|-------------|
| `std` (default) | Standard library support and IDNA domain normalization |
| `alloc` | Allocation support without `std`, including IDNA domain normalization |
| `serde` | `serde_core`-backed serialization/deserialization for addresses, parts, and parsing options; combine with `alloc` or `std` for IDNA-validated A-label deserialization |
| `clap` | `clap::Args` / `ValueEnum` support for parsing options; CLI flags only, no environment-variable wiring |
| `zeroize` | `zeroize::Zeroize` support for `Buffer`, `EmailAddr`, `LocalPart`, and `DomainPart` when the selected storage can be wiped |
| `bytes` / `bytes_1` | `bytes::Bytes` storage support |
| `smallvec` / `smallvec_1` | `smallvec::SmallVec` byte storage support |
| `smol_str` / `smol_str_0_3` | `smol_str::SmolStr` storage support |
| `tinyvec` / `tinyvec_1` | `tinyvec::TinyVec` byte storage support |
| `triomphe` / `triomphe_0_1` | `triomphe::Arc<str>` and `triomphe::Arc<[u8]>` storage support |
| `arbitrary` | Invariant-preserving `arbitrary::Arbitrary` impls for owned storage backends |
| `quickcheck` | Invariant-preserving `quickcheck::Arbitrary` impls for owned storage backends |
Calling `zeroize` clears the underlying storage and intentionally destroys the
validated-address invariant. Drop or reassign a value after wiping it.
## RFC Coverage
`emailaddr` validates the address forms relevant to an address value:
- RFC 5321 SMTP mailbox syntax and length limits
- RFC 5322 `addr-spec` local-part/domain token grammar where it overlaps with
mailbox addresses
- RFC 6531 SMTPUTF8 local-parts
- RFC 6532 UTF-8 header character model for address tokens
- RFC 1123 host label digit relaxation for domains
- RFC 4291 IPv6 address literals
- RFC 5890 IDNA domain normalization
- RFC 3629 UTF-8 validity for SMTPUTF8 input
- RFC 5234 ABNF-derived parser behavior
- RFC 3696 application-level email length guidance
This crate does not parse full message headers, display names, groups, comments,
or SMTP commands; it validates and normalizes address values.
## License
`emailaddr` is under the terms of both the MIT license and the Apache License
(Version 2.0).
See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT) for details.
Copyright (c) 2026 Al Liu.
[Github-url]: https://github.com/al8n/emailaddr/
[CI-url]: https://github.com/al8n/emailaddr/actions/workflows/ci.yml
[codecov-url]: https://codecov.io/gh/al8n/emailaddr
[doc-url]: https://docs.rs/emailaddr
[crates-url]: https://crates.io/crates/emailaddr
[discord]: https://discord.gg/twJBXUNbVn