emval/
lib.rs

1//! # 📬 emval
2//!
3//! `emval` is a blazingly fast email validator written in Rust with Python bindings, offering performance improvements of 100-1000x over traditional validators.
4//!
5//! ![performance image](https://raw.githubusercontent.com/bnkc/emval/b90cc4a0ae24e329702872c4fb1cccf212d556a6/perf.svg)
6
7//! ## Features
8
9//! - Drop-in replacement for popular email validators like `python-email-validator`, `verify-email`, and `pyIsEmail`.
10//! - 100-1000x faster than [python-email-validator](https://github.com/JoshData/python-email-validator).
11//! - Validates email address syntax according to [RFC 5322](https://www.rfc-editor.org/rfc/rfc5322.html) and [RFC 6531](https://www.rfc-editor.org/rfc/rfc6531.html).
12//! - Checks domain deliverability (coming soon).
13//! - Supports internationalized domain names (IDN) and local parts.
14//! - Provides user-friendly syntax errors.
15//! - Normalizes addresses.
16//! - Rejects invalid and unsafe Unicode characters.
17//!
18//! ## Getting Started
19//!
20//! Install `emval` from PyPI:
21//!
22//! ```sh
23//! pip install emval
24//! ```
25//!
26//! or use `emval` in a Rust project:
27//! ```sh
28//! cargo add emval
29//! ```
30//!
31//! ## Usage
32//!
33//! ### Quick Start
34//!
35//! To validate an email address in Python:
36//!
37//! ```python
38//! from emval import validate_email, EmailValidator
39//!
40//! email = "example@domain.com"
41//!
42//! try:
43//!     # Check if the email is valid.
44//!     val_email = validate_email(email)
45//!     # Utilize the normalized form for storage.
46//!     normalized_email = val_email.normalized
47//! except Exception as e:
48//!     # Example: "Invalid Local Part: Quoting the local part before the '@' sign is not permitted in this context."
49//!     print(str(e))
50//! ```
51//!
52//! The same code in Rust:
53//! ```rust
54//! use emval::{validate_email, ValidationError};
55//!
56//! fn main() -> Result<(), ValidationError> {
57//!     let email = "example@domain.com";
58//!     let val_email = validate_email(email)?;
59//!     let normalized_email = val_email.normalized;
60//!     Ok(())
61//! }
62//! ```
63//!
64//! ### Configurations
65//!
66//! Customize email validation behavior using the `EmailValidator` class:
67//!
68//! ```python
69//! from emval import EmailValidator
70//!
71//! emval = EmailValidator(
72//!     allow_smtputf8=False,
73//!     allow_empty_local=True,
74//!     allow_quoted_local=True,
75//!     allow_domain_literal=True,
76//!     deliverable_address=False,
77//! )
78//!
79//! email = "user@[192.168.1.1]"
80//!
81//! try:
82//!     validated_email = emval.validate_email(email)
83//!     print(validated_email)
84//! except Exception as e:
85//!     print(str(e))
86//! ```
87//!
88//! The same code in Rust:
89//! ```rust
90//! use emval::{EmailValidator, ValidationError};
91//!
92//! fn main() -> Result<(), ValidationError> {
93//!     let emval = EmailValidator {
94//!         allow_smtputf8: false,
95//!         allow_empty_local: true,
96//!         allow_quoted_local: true,
97//!         allow_domain_literal: true,
98//!         deliverable_address: false,
99//!         allowed_special_domains: Vec::new(),
100//!     };
101//!
102//!     let email = "example@domain.com";
103//!     let validated_email = emval.validate_email(email)?;
104//!     Ok(())
105//! }
106//! ```
107//!
108//! ### Options
109//!
110//! - `allow_smtputf8`: Allows internationalized email addresses.
111//! - `allow_empty_local`: Allows an empty local part (e.g., `@domain.com`).
112//! - `allow_quoted_local`: Allows quoted local parts (e.g., `"user name"@domain.com`).
113//! - `allow_domain_literal`: Allows domain literals (e.g., `[192.168.0.1]`).
114//! - `deliverable_address`: Checks if the email address is deliverable by verifying the domain's MX records.
115//!
116//! ## Technical Details
117//!
118//! ### Email Address Syntax
119//!
120//! emval adheres to the syntax rules defined in [RFC 5322](https://www.rfc-editor.org/rfc/rfc5322.html) and [RFC 6531](https://www.rfc-editor.org/rfc/rfc6531.html). It supports both ASCII and internationalized characters.
121//!
122//! ### Internationalized Email Addresses
123//!
124//! #### Domain Names
125//!
126//! emval converts non-ASCII domain names into their ASCII "Punycode" form according to [IDNA 2008](https://www.rfc-editor.org/rfc/rfc5891.html). This ensures compatibility with systems that do not support Unicode.
127//!
128//! #### Local Parts
129//!
130//! emval allows international characters in the local part of email addresses, following [RFC 6531](https://www.rfc-editor.org/rfc/rfc6531.html). It offers options to handle environments without SMTPUTF8 support.
131//!
132//! ### Unsafe Unicode Characters
133//!
134//! emval rejects unsafe Unicode characters to enhance security, preventing display and interpretation issues.
135//!
136//! ### Normalization
137//!
138//! emval normalizes email addresses to ensure consistency:
139//!
140//! - **Lowercasing domains:** Domain names are standardized to lowercase.
141//! - **Unicode NFC normalization:** Characters are transformed into their precomposed forms.
142//! - **Removing unnecessary characters:** Quotes and backslashes in the local part are removed.
143//!
144//! ## Acknowledgements
145//!
146//! This project draws inspiration from [python-email-validator](https://github.com/JoshData/python-email-validator). While `python-email-validator` is more comprehensive, `emval` aims to provide a faster solution.
147//!
148//! ## Getting Help
149//!
150//! For questions and issues, please open an issue in the [GitHub issue tracker](https://github.com/bnkc/emval/issues).
151//!
152//! ## License
153//!
154//! emval is licensed under the [MIT License](https://opensource.org/licenses/MIT). See the [LICENSE](https://github.com/bnkc/emval/blob/main/LICENSE) file for more details.
155
156mod consts;
157pub mod errors;
158mod models;
159mod polars_plugin;
160pub(crate) mod util;
161mod validators;
162
163pub use crate::errors::ValidationError;
164pub use crate::models::{EmailValidator, ValidatedEmail};
165
166/// Validate an email with default validator settings.
167pub fn validate_email<T: AsRef<str>>(email: T) -> Result<ValidatedEmail, ValidationError> {
168    let validator = EmailValidator::default();
169    validator.validate_email(email.as_ref())
170}
171
172use pyo3::prelude::*;
173
174#[pymodule]
175fn _emval(_py: Python, m: &Bound<PyModule>) -> PyResult<()> {
176    m.add_class::<models::EmailValidator>()?;
177    m.add_class::<models::ValidatedEmail>()?;
178
179    Ok(())
180}