Expand description
emailaddr
Type-safe, zero-copy, validated email addresses with generic storage, IDNA domain support, and no_std/no-alloc compatibility.
§Overview
emailaddr provides validated email address types with a storage model similar
to 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.Bufferstores a full validated address on the stack forno_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
[dependencies]
emailaddr = "0.1"§Quick Start
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
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
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
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.
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:
[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-speclocal-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-MIT for details.
Copyright (c) 2026 Al Liu.
Structs§
- Buffer
- A stack-allocated buffer for a validated email address.
- Domain
Options - Domain-part parsing options.
- Domain
Part - A validated email domain-part.
- Email
Addr - A type-safe, validated email address.
- Limits
- Parsing limits.
- Local
Options - Local-part parsing options.
- Local
Part - A validated email local-part.
- Options
- Email address parsing options.
- Parse
Domain Part Error - The provided input is not a syntactically valid email domain-part.
- Parse
Local Part Error - The provided input is not a syntactically valid email local-part.
- Relax
- Relaxed email address policy for custom parsing options.
- Strict
- Strict email address policy.
Enums§
- Domain
Literal Policy - Policy for address-literal domain-parts.
- Domain
Unicode Policy - Policy for Unicode domain input.
- Parse
Email Addr Error - The provided input is not a syntactically valid email address.
- Smtp
Utf8 Policy - Policy for SMTPUTF8 local-parts.
Constants§
- DEFAULT_
MAX_ LOCAL_ PART_ LENGTH - Default local-part length limit used by
Limits. - DEFAULT_
MINIMUM_ DNS_ LABELS - Default minimum DNS label count used by
DomainOptions. - MAX_
DOMAIN_ PART_ LENGTH - The maximum DNS domain length accepted for email domains.
- MAX_
EMAIL_ ADDR_ LENGTH - The maximum email address length accepted by this crate.
- MAX_
LOCAL_ PART_ LENGTH - The maximum local-part length, in bytes.
Traits§
- Email
Addr Serde Storage serde - Storage that can expose a validated email address, local-part, or domain-part as a UTF-8 string for serde serialization.
Functions§
- is_
atext - Returns
trueifbyteis valid in an unquoted email atom. - verify_
ascii_ dns_ domain - Verifies that
inputis a valid ASCII DNS domain name. - verify_
ascii_ domain_ part - Verifies that
inputis a valid ASCII email domain-part. - verify_
ascii_ email_ addr - Verifies that
inputis a valid ASCII email address. - verify_
ascii_ local_ part - Verifies that
inputis a valid ASCII email local-part. - verify_
email_ addr allocorstd - Verifies that
inputis a valid email address. - verify_
email_ addr_ with_ options - Verifies that
inputis a valid email address under custom parsing options. - verify_
local_ part - Verifies that
inputis a valid email local-part.
Type Aliases§
- Relaxed
Parse Output allocorstd - Output from relaxed parsing that may preserve the original storage or return a stack buffer.