Skip to content

Repository files navigation

email-address-validator

Go Reference CI

Package email checks an address against the Mailbox grammar of RFC 5321, the production an address must satisfy to appear in an SMTP MAIL or RCPT command. Validators commonly approximate this grammar with a regular expression; this package implements it directly, along with the length limits and errata that accompany it.

What it checks

  • The RFC 5321 Mailbox grammar: dot-string and quoted-string local parts, letter-digit-hyphen domain labels, and IPv4 and IPv6 address literals.
  • Length limits from RFC 5321 section 4.5.3.1 as corrected by RFC 3696 erratum 1690: a 64-octet local part, a 255-octet domain, and a 254-octet address.
  • With ValidateSMTPUTF8, the internationalization extensions of RFC 6531 and RFC 6532: well-formed UTF-8 in the local part and IDNA2008 U-labels in the domain, including the RFC 5892 derived-property and contextual rules.

Validation is syntactic. The package does not resolve DNS, apply IDN registry policy, or confirm that a mailbox exists. SPEC.md records the governing specifications, the rules enforced, and the judgment calls behind them.

Install

go get github.com/initialcapacity/email-address-validator

The import path ends in email-address-validator; the package name is email:

import email "github.com/initialcapacity/email-address-validator"

Usage

Validate and ValidateSMTPUTF8 return nil for a valid address. IsValid and IsValidSMTPUTF8 are boolean forms of the same checks.

if err := email.Validate("grace.hopper@example.com"); err != nil {
    log.Fatal(err)
}

email.IsValid("用户@example.com")          // false: non-ASCII requires SMTPUTF8
email.IsValidSMTPUTF8("用户@example.com") // true

Every failure is a *email.SyntaxError wrapping a sentinel error. Classify failures with errors.Is, then recover the byte offset of the fault with errors.As:

err := email.Validate("ada@exa_mple.com")

if errors.Is(err, email.ErrInvalidDomain) {
    var syntaxErr *email.SyntaxError
    if errors.As(err, &syntaxErr) {
        fmt.Println(syntaxErr.Offset) // 7, the underscore
    }
}

The package documentation lists the sentinel errors and holds runnable examples.

Reimplementing in another language

The repository doubles as a porting kit. REIMPLEMENTING.md lists what a port needs, and testdata/cases.json holds a language-agnostic conformance suite that CI runs against this implementation.

To start a port, give a coding agent this prompt, filling in the language:

Reimplement the email address validator from https://github.com/initialcapacity/email-address-validator in <language>. Clone or fetch the repository, then read REIMPLEMENTING.md, SPEC.md, and testdata/README.md, in that order, along with the RFC sections they cite. The Go source is the reference implementation.

Implement both validation modes: strict RFC 5321 (ASCII) and RFC 6531 (SMTPUTF8). Expose a boolean check and a diagnostic form that reports an error kind and a byte offset. Accept byte-string input so the base64-encoded cases run.

Write a conformance test that runs every case in testdata/cases.json through both modes, and iterate until both tiers pass in full: the accept/reject results must match valid.ascii and valid.smtputf8, and the diagnostics must match every errors entry, kind and offset. Fix the implementation, never the expectations, and do not special-case any vector. Use idiomatic tooling and project structure for <language>, and keep third-party dependencies to what the standard library cannot provide.

Development

Build and test with the standard Go toolchain, version 1.27 or later:

go build ./...
go test -race ./...

Before sending a change, run the same checks CI runs:

test -z "$(gofmt -l .)"
go vet ./...
go test -race ./...

unicode_tables.go holds case folding, Joining_Type, and virama data generated from the Unicode Character Database. A test fails when its version drifts from the standard library's; after a toolchain upgrade, regenerate it:

go run gen_unicode_tables.go -ucd /path/to/ucd -version 17.0.0

A fuzz target exercises the parser with arbitrary input and asserts that RFC 5321 validity implies RFC 6531 validity. CI runs it for 30 seconds; run it longer when changing the parser:

go test -fuzz=FuzzValidate -fuzztime=5m .

License

MIT; see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages