diff --git a/Cargo.lock b/Cargo.lock index 1f128f9..fb1c6b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -469,6 +469,7 @@ dependencies = [ "trust-dns-resolver", "unicode-properties", "unicode_names2", + "wasm-bindgen", ] [[package]] @@ -1125,9 +1126,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", @@ -3317,9 +3318,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", @@ -3328,25 +3329,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.54" +version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", @@ -3357,9 +3344,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3367,22 +3354,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] @@ -3402,9 +3389,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.81" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index e851e36..2f5447f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,10 @@ name = "emval" crate-type = ["cdylib", "rlib"] [dependencies] -pyo3 = { version = "=0.25.1", features = ["extension-module", "abi3-py38"] } +pyo3 = { version = "=0.25.1", features = [ + "extension-module", + "abi3-py38", +], optional = true } # emval dependencies icu_normalizer = "2.0.0" @@ -26,14 +29,29 @@ regex = "1.10.5" unicode-properties = "0.1.1" rstest = "0.21.0" unicode_names2 = "1.2.2" -trust-dns-resolver = "0.23.2" +trust-dns-resolver = { version = "0.23.2", optional = true } +# WASM dependencies +wasm-bindgen = { version = "0.2", optional = true } # polars dependencies -pyo3-polars = { version = "0.22", features = ["derive", "dtype-struct"] } -polars = { version = "0.49.1", features = ["dtype-struct"], default-features = false } -polars-arrow = { version = "0.49.1", default-features = false } +pyo3-polars = { version = "0.22", features = [ + "derive", + "dtype-struct", +], optional = true } +polars = { version = "0.49.1", features = [ + "dtype-struct", +], default-features = false, optional = true } +polars-arrow = { version = "0.49.1", default-features = false, optional = true } serde = { version = "1", features = ["derive"] } +[features] +default = ["python", "polars"] +python = ["dep:pyo3", "dep:pyo3-polars", "dep:polars", "dep:polars-arrow"] +polars = ["dep:pyo3-polars", "dep:polars", "dep:polars-arrow", "dns"] +dns = ["dep:trust-dns-resolver"] +wasm = ["dep:wasm-bindgen"] + [profile.release] lto = true + diff --git a/package.json b/package.json new file mode 100644 index 0000000..b293bac --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "emval", + "version": "0.1.12", + "type": "module", + "main": "js/emval.js", + "types": "js/emval.d.ts", + "files": [ + "js/" + ], + "scripts": { + "cargo:debug": "cargo build --no-default-features --features wasm --target wasm32-unknown-unknown", + "cargo": "cargo build --no-default-features --features wasm --target wasm32-unknown-unknown --release", + "wasm": "wasm-bindgen target/wasm32-unknown-unknown/release/emval.wasm --out-dir js --target bundler", + "wasm:debug": "wasm-bindgen target/wasm32-unknown-unknown/debug/emval.wasm --out-dir js --target bundler", + "build:debug": "npm run cargo:debug && npm run wasm:debug", + "build": "npm run cargo && npm run wasm" + } +} diff --git a/src/errors.rs b/src/errors.rs index 657e365..126e657 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,4 +1,6 @@ +#[cfg(feature = "python")] use pyo3::exceptions::{PySyntaxError, PyValueError}; +#[cfg(feature = "python")] use pyo3::prelude::*; /// An error enum for email validation. @@ -10,6 +12,7 @@ pub enum ValidationError { ValueError(String), } +#[cfg(feature = "python")] impl From for PyErr { fn from(err: ValidationError) -> Self { match err { diff --git a/src/lib.rs b/src/lib.rs index f8cc371..221c69b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,10 +156,16 @@ mod consts; pub mod errors; mod models; +#[cfg(feature = "polars")] mod polars_plugin; pub(crate) mod util; mod validators; +#[cfg(feature = "wasm")] +use wasm_bindgen::prelude::*; + + + pub use crate::errors::ValidationError; pub use crate::models::{EmailValidator, ValidatedEmail}; @@ -169,8 +175,19 @@ pub fn validate_email>(email: T) -> Result Result { + match validate_email(&email) { + Ok(validated) => Ok(validated.normalized), + Err(e) => Err(e.to_string()), + } +} + +#[cfg(feature = "python")] use pyo3::prelude::*; +#[cfg(feature = "python")] #[pymodule] fn _emval(_py: Python, m: &Bound) -> PyResult<()> { m.add_class::()?; diff --git a/src/models.rs b/src/models.rs index f91a11f..294fa1a 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,37 +1,38 @@ +#[cfg(feature = "python")] use pyo3::prelude::*; use std::net::IpAddr; /// A structure representing a validated email address with various components and normalized forms. -#[pyclass] +#[cfg_attr(feature = "python", pyclass)] pub struct ValidatedEmail { /// The email address provided to validate_email. - #[pyo3(get)] + #[cfg_attr(feature = "python", 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)] + #[cfg_attr(feature = "python", get)] pub normalized: String, /// The ASCII (Punycode-encoded) form of the email address, if one exists. - #[pyo3(get)] + #[cfg_attr(feature = "python", get)] pub ascii_email: Option, /// The local part of the email address (the part before the '@' sign) after it has been Unicode normalized. - #[pyo3(get)] + #[cfg_attr(feature = "python", get)] pub local_part: String, /// If the domain part is a domain literal, it will be an IPv4Address or IPv6Address object. - #[pyo3(get)] + #[cfg_attr(feature = "python", get)] pub domain_address: Option, /// The domain part of the email address (the part after the '@' sign) after Unicode normalization. - #[pyo3(get)] + #[cfg_attr(feature = "python", get)] pub domain_name: String, /// The ASCII (Punycode-encoded) form of the domain part of the email address. - #[pyo3(get)] + #[cfg_attr(feature = "python", get)] pub ascii_domain: String, /// Whether the email address is deliverable. - #[pyo3(get)] + #[cfg_attr(feature = "python", get)] pub is_deliverable: bool, } /// A structure for customizing email validation. -#[pyclass] +#[cfg_attr(feature = "python", pyclass)] pub struct EmailValidator { /// Whether to allow SMTPUTF8. [Default: true] pub allow_smtputf8: bool, diff --git a/src/util/mod.rs b/src/util/mod.rs index be10d07..e01a4d5 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1 +1,2 @@ +#[cfg(feature = "dns")] pub mod ip_addr_ext; diff --git a/src/validators/domain.rs b/src/validators/domain.rs index 3a47b99..af29072 100644 --- a/src/validators/domain.rs +++ b/src/validators/domain.rs @@ -4,8 +4,11 @@ use idna::uts46::Uts46; use idna::uts46::{AsciiDenyList, DnsLength, Hyphens}; use std::net::IpAddr; use std::str::FromStr; +#[cfg(feature = "dns")] use trust_dns_resolver::config::*; +#[cfg(feature = "dns")] use trust_dns_resolver::Resolver; +#[cfg(feature = "dns")] use crate::util::ip_addr_ext::IpAddrExt; pub fn validate_domain( @@ -193,6 +196,7 @@ pub fn validate_domain( } } +#[cfg(feature = "dns")] pub fn validate_deliverability(domain: &str) -> Result<(), ValidationError> { let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()) .map_err(|e| ValidationError::SyntaxError(e.to_string()))?; @@ -244,6 +248,12 @@ pub fn validate_deliverability(domain: &str) -> Result<(), ValidationError> { )) } +#[cfg(not(feature = "dns"))] +pub fn validate_deliverability(_domain: &str) -> Result<(), ValidationError> { + // DNS resolution is not available when dns feature is disabled + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/validators/email.rs b/src/validators/email.rs index 9997533..c425a6c 100644 --- a/src/validators/email.rs +++ b/src/validators/email.rs @@ -1,4 +1,5 @@ use crate::models::{EmailValidator, ValidatedEmail}; +#[cfg(feature = "python")] use pyo3::prelude::*; impl EmailValidator { @@ -55,6 +56,7 @@ impl EmailValidator { } } +#[cfg(feature = "python")] #[pymethods] impl EmailValidator { /// Create a new email validator with the given settings.