-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from denehoffman/rust-crate
feat: expose Rust API
- Loading branch information
Showing
8 changed files
with
371 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
name: Release-plz | ||
|
||
permissions: | ||
pull-requests: write | ||
contents: write | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
|
||
jobs: | ||
|
||
# Release unpublished packages. | ||
release-plz-release: | ||
name: Release-plz release | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 | ||
with: | ||
fetch-depth: 0 | ||
- name: Install Rust toolchain | ||
uses: dtolnay/rust-toolchain@stable | ||
- name: Run release-plz | ||
uses: release-plz/[email protected] | ||
with: | ||
command: release | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | ||
|
||
# Create a PR with the new versions and changelog, preparing the next release. | ||
release-plz-pr: | ||
name: Release-plz PR | ||
runs-on: ubuntu-latest | ||
concurrency: | ||
group: release-plz-${{ github.ref }} | ||
cancel-in-progress: false | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 | ||
with: | ||
fetch-depth: 0 | ||
- name: Install Rust toolchain | ||
uses: dtolnay/rust-toolchain@stable | ||
- name: Run release-plz | ||
uses: release-plz/[email protected] | ||
with: | ||
command: release-pr | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
# 📬 emval | ||
|
||
`emval` is a blazingly fast Python email validator written in Rust, offering performance improvements of 100-1000x over traditional validators. | ||
`emval` is a blazingly fast email validator written in Rust with Python bindings, offering performance improvements of 100-1000x over traditional validators. | ||
|
||
![performance image](https://raw.githubusercontent.com/bnkc/emval/b90cc4a0ae24e329702872c4fb1cccf212d556a6/perf.svg) | ||
|
||
|
@@ -23,11 +23,16 @@ Install `emval` from PyPI: | |
pip install emval | ||
``` | ||
|
||
or use `emval` in a Rust project: | ||
```sh | ||
cargo add emval | ||
``` | ||
|
||
## Usage | ||
|
||
### Quick Start | ||
|
||
To validate an email address: | ||
To validate an email address in Python: | ||
|
||
```python | ||
from emval import validate_email, EmailValidator | ||
|
@@ -44,6 +49,18 @@ except Exception as e: | |
print(str(e)) | ||
``` | ||
|
||
The same code in Rust: | ||
```rust | ||
use emval::{validate_email, ValidationError}; | ||
|
||
fn main() -> Result<(), ValidationError> { | ||
let email = "[email protected]"; | ||
let val_email = validate_email(email)?; | ||
let normalized_email = val_email.normalized; | ||
Ok(()) | ||
} | ||
``` | ||
|
||
### Configurations | ||
|
||
Customize email validation behavior using the `EmailValidator` class: | ||
|
@@ -68,6 +85,25 @@ except Exception as e: | |
print(str(e)) | ||
``` | ||
|
||
The same code in Rust: | ||
```rust | ||
use emval::{EmailValidator, ValidationError}; | ||
|
||
fn main() -> Result<(), ValidationError> { | ||
let emval = EmailValidator { | ||
allow_smtputf8: false, | ||
allow_empty_local: true, | ||
allow_quoted_local: true, | ||
allow_domain_literal: true, | ||
deliverable_address: false, | ||
}; | ||
|
||
let email = "[email protected]"; | ||
let validated_email = emval.validate_email(email)?; | ||
Ok(()) | ||
} | ||
``` | ||
|
||
### Options | ||
|
||
- `allow_smtputf8`: Allows internationalized email addresses. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,174 @@ | ||
//! # 📬 emval | ||
//! | ||
//! `emval` is a blazingly fast email validator written in Rust with Python bindings, offering performance improvements of 100-1000x over traditional validators. | ||
//! | ||
//! ![performance image](https://raw.githubusercontent.com/bnkc/emval/b90cc4a0ae24e329702872c4fb1cccf212d556a6/perf.svg) | ||
//! ## Features | ||
//! - Drop-in replacement for popular email validators like `python-email-validator`, `verify-email`, and `pyIsEmail`. | ||
//! - 100-1000x faster than [python-email-validator](https://github.com/JoshData/python-email-validator). | ||
//! - 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). | ||
//! - Checks domain deliverability (coming soon). | ||
//! - Supports internationalized domain names (IDN) and local parts. | ||
//! - Provides user-friendly syntax errors. | ||
//! - Normalizes addresses. | ||
//! - Rejects invalid and unsafe Unicode characters. | ||
//! | ||
//! ## Getting Started | ||
//! | ||
//! Install `emval` from PyPI: | ||
//! | ||
//! ```sh | ||
//! pip install emval | ||
//! ``` | ||
//! | ||
//! or use `emval` in a Rust project: | ||
//! ```sh | ||
//! cargo add emval | ||
//! ``` | ||
//! | ||
//! ## Usage | ||
//! | ||
//! ### Quick Start | ||
//! | ||
//! To validate an email address in Python: | ||
//! | ||
//! ```python | ||
//! from emval import validate_email, EmailValidator | ||
//! | ||
//! email = "[email protected]" | ||
//! | ||
//! try: | ||
//! # Check if the email is valid. | ||
//! val_email = validate_email(email) | ||
//! # Utilize the normalized form for storage. | ||
//! normalized_email = val_email.normalized | ||
//! except Exception as e: | ||
//! # Example: "Invalid Local Part: Quoting the local part before the '@' sign is not permitted in this context." | ||
//! print(str(e)) | ||
//! ``` | ||
//! | ||
//! The same code in Rust: | ||
//! ```rust | ||
//! use emval::{validate_email, ValidationError}; | ||
//! | ||
//! fn main() -> Result<(), ValidationError> { | ||
//! let email = "[email protected]"; | ||
//! let val_email = validate_email(email)?; | ||
//! let normalized_email = val_email.normalized; | ||
//! Ok(()) | ||
//! } | ||
//! ``` | ||
//! | ||
//! ### Configurations | ||
//! | ||
//! Customize email validation behavior using the `EmailValidator` class: | ||
//! | ||
//! ```python | ||
//! from emval import EmailValidator | ||
//! | ||
//! emval = EmailValidator( | ||
//! allow_smtputf8=False, | ||
//! allow_empty_local=True, | ||
//! allow_quoted_local=True, | ||
//! allow_domain_literal=True, | ||
//! deliverable_address=False, | ||
//! ) | ||
//! | ||
//! email = "user@[192.168.1.1]" | ||
//! | ||
//! try: | ||
//! validated_email = emval.validate_email(email) | ||
//! print(validated_email) | ||
//! except Exception as e: | ||
//! print(str(e)) | ||
//! ``` | ||
//! | ||
//! The same code in Rust: | ||
//! ```rust | ||
//! use emval::{EmailValidator, ValidationError}; | ||
//! | ||
//! fn main() -> Result<(), ValidationError> { | ||
//! let emval = EmailValidator { | ||
//! allow_smtputf8: false, | ||
//! allow_empty_local: true, | ||
//! allow_quoted_local: true, | ||
//! allow_domain_literal: true, | ||
//! deliverable_address: false, | ||
//! }; | ||
//! | ||
//! let email = "[email protected]"; | ||
//! let validated_email = emval.validate_email(email)?; | ||
//! Ok(()) | ||
//! } | ||
//! ``` | ||
//! | ||
//! ### Options | ||
//! | ||
//! - `allow_smtputf8`: Allows internationalized email addresses. | ||
//! - `allow_empty_local`: Allows an empty local part (e.g., `@domain.com`). | ||
//! - `allow_quoted_local`: Allows quoted local parts (e.g., `"user name"@domain.com`). | ||
//! - `allow_domain_literal`: Allows domain literals (e.g., `[192.168.0.1]`). | ||
//! - `deliverable_address`: Checks if the email address is deliverable by verifying the domain's MX records. | ||
//! | ||
//! ## Technical Details | ||
//! | ||
//! ### Email Address Syntax | ||
//! | ||
//! 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. | ||
//! | ||
//! ### Internationalized Email Addresses | ||
//! | ||
//! #### Domain Names | ||
//! | ||
//! 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. | ||
//! | ||
//! #### Local Parts | ||
//! | ||
//! 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. | ||
//! | ||
//! ### Unsafe Unicode Characters | ||
//! | ||
//! emval rejects unsafe Unicode characters to enhance security, preventing display and interpretation issues. | ||
//! | ||
//! ### Normalization | ||
//! | ||
//! emval normalizes email addresses to ensure consistency: | ||
//! | ||
//! - **Lowercasing domains:** Domain names are standardized to lowercase. | ||
//! - **Unicode NFC normalization:** Characters are transformed into their precomposed forms. | ||
//! - **Removing unnecessary characters:** Quotes and backslashes in the local part are removed. | ||
//! | ||
//! ## Acknowledgements | ||
//! | ||
//! 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. | ||
//! | ||
//! ## Getting Help | ||
//! | ||
//! For questions and issues, please open an issue in the [GitHub issue tracker](https://github.com/bnkc/emval/issues). | ||
//! | ||
//! ## License | ||
//! | ||
//! 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. | ||
#![feature(ip)] | ||
#[macro_use] | ||
extern crate lazy_static; | ||
mod consts; | ||
mod errors; | ||
pub mod errors; | ||
mod models; | ||
mod validators; | ||
|
||
pub use crate::errors::ValidationError; | ||
pub use crate::models::{EmailValidator, ValidatedEmail}; | ||
|
||
/// Validate an email with default validator settings. | ||
pub fn validate_email<T: AsRef<str>>(email: T) -> Result<ValidatedEmail, ValidationError> { | ||
let validator = EmailValidator::default(); | ||
validator.validate_email(email.as_ref()) | ||
} | ||
|
||
use pyo3::prelude::*; | ||
|
||
#[pymodule] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,52 @@ | ||
use pyo3::prelude::*; | ||
use std::net::IpAddr; | ||
|
||
/// A structure representing a validated email address with various components and normalized forms. | ||
#[pyclass] | ||
pub struct ValidatedEmail { | ||
/// The email address provided to validate_email. | ||
#[pyo3(get)] | ||
pub original: String, | ||
/// The normalized email address should be used instead of the original. It converts IDNA ASCII domain names to Unicode and normalizes both the local part and domain. The normalized address combines the local part and domain name with an '@' sign. | ||
#[pyo3(get)] | ||
pub normalized: String, | ||
/// The local part of the email address (the part before the '@' sign) after it has been Unicode normalized. | ||
#[pyo3(get)] | ||
pub local_part: String, | ||
/// If the domain part is a domain literal, it will be an IPv4Address or IPv6Address object. | ||
#[pyo3(get)] | ||
pub domain_address: Option<IpAddr>, | ||
/// The domain part of the email address (the part after the '@' sign) after Unicode normalization. | ||
#[pyo3(get)] | ||
pub domain_name: String, | ||
/// Whether the email address is deliverable. | ||
#[pyo3(get)] | ||
pub is_deliverable: bool, | ||
} | ||
|
||
#[derive(Default)] | ||
/// A structure for customizing email validation. | ||
#[pyclass] | ||
pub struct EmailValidator { | ||
/// Whether to allow SMTPUTF8. [Default: true] | ||
pub allow_smtputf8: bool, | ||
/// Whether to allow empty local part. [Default: false] | ||
pub allow_empty_local: bool, | ||
/// Whether to allow quoted local part. [Default: false] | ||
pub allow_quoted_local: bool, | ||
/// Whether to allow domain literals. [Default: false] | ||
pub allow_domain_literal: bool, | ||
/// Whether to check if the email address is deliverable. [Default: true] | ||
pub deliverable_address: bool, | ||
} | ||
|
||
impl Default for EmailValidator { | ||
fn default() -> Self { | ||
Self { | ||
allow_smtputf8: true, | ||
allow_empty_local: false, | ||
allow_quoted_local: false, | ||
allow_domain_literal: false, | ||
deliverable_address: true, | ||
} | ||
} | ||
} |
Oops, something went wrong.