Skip to content

Commit

Permalink
WIP: allow to switch between regex and regex-lite
Browse files Browse the repository at this point in the history
  • Loading branch information
jirutka committed Jul 29, 2023
1 parent 6313e7f commit f613346
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 21 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,15 +278,17 @@ The `ListErrorBuilder::push` and `ListErrorBuilder::insert` methods will ignore
|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
| `derive` | Enables the usage of the `derive(Validate)` macro | [`garde_derive`](https://crates.io/crates/garde_derive) |
| `url` | Validation of URLs via the `url` crate. | [`url`](https://crates.io/crates/url) |
| `email` | Validation of emails according to [HTML5](https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address) | [`regex`](https://crates.io/crates/regex), [`once_cell`](https://crates.io/crates/once_cell) |
| `email` | Validation of emails according to [HTML5](https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address) | [`regex`](https://crates.io/crates/regex) or [`regex-lite`](https://crates.io/crates/regex-lite), [`once_cell`](https://crates.io/crates/once_cell) |
| `email-idna` | Support for [Internationalizing Domain Names for Applications](https://url.spec.whatwg.org/#idna) in email addresses | [`idna`](https://crates.io/crates/idna) |
| `pattern` | Validation using regular expressions via the `regex` crate | [`regex`](https://crates.io/crates/regex), [`once_cell`](https://crates.io/crates/once_cell) |
| `pattern` | Validation using regular expressions via the `regex` crate | [`regex`](https://crates.io/crates/regex) or [`regex-lite`](https://crates.io/crates/regex-lite), [`once_cell`](https://crates.io/crates/once_cell) |
| `credit-card` | Validation of credit card numbers via the `card-validate` crate | [`card-validate`](https://crates.io/crates/card-validate) |
| `phone-number` | Validation of phone numbers via the `phonenumber` crate | [`phonenumber`](https://crates.io/crates/phonenumber) |
| `regex-lite` | Adds support for the `regex-lite` crate for the pattern validator and switches the email validator to use `regex-lite` instead of `regex` | [`regex-lite`](https://crates.io/crates/regex-lite) |
| `regex-full` | Use the `regex` crate in the pattern and email validator | [`regex`](https://crates.io/crates/regex) |
| `regex-lite` | Use the `regex-lite` crate in the pattern and email validator | [`regex-lite`](https://crates.io/crates/regex-lite) |

Additional notes:
- Enabling the `regex-lite` feature does not remove the dependency on the `regex` crate, but if you avoid using `#[garde(pattern(...)]` with a string literal, then `regex` will not be used and may be removed by link-time optimisation.
- If you enable the `email` or `pattern` feature, you must also enable either `regex-full` or `regex-lite`.
- If both `regex-full` and `regex-lite` are enabled, then the `regex-lite` support will be built, but the email validator and the pattern with a string literal will use the `regex` crate, not `regex-lite`.

### Why `garde`?

Expand Down
9 changes: 6 additions & 3 deletions garde/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,19 @@ default = [
"email",
"email-idna",
"pattern",
"regex-full",
]
serde = ["dep:serde"]
derive = ["dep:garde_derive"]
url = ["dep:url"]
credit-card = ["dep:card-validate"]
phone-number = ["dep:phonenumber"]
email = ["dep:regex", "dep:once_cell"]
# Requires regex-full or regex-lite.
email = ["dep:once_cell"]
email-idna = ["dep:idna"]
pattern = ["dep:regex", "dep:once_cell", "garde_derive?/regex"]
regex-lite = ["dep:regex-lite"]
pattern = ["dep:once_cell"]
regex-full = ["dep:regex", "garde_derive?/regex-full"]
regex-lite = ["dep:regex-lite", "garde_derive?/regex-lite"]

[dependencies]
garde_derive = { version = "0.13.0", path = "../garde_derive", optional = true, default-features = false }
Expand Down
18 changes: 18 additions & 0 deletions garde/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
fn main() {
#[cfg(all(
feature = "email",
not(feature = "regex-full"),
not(feature = "regex-lite")
))]
panic!("regex-full or regex-lite feature must be enabled when email feature is enabled");

#[cfg(all(
feature = "pattern",
not(feature = "regex-full"),
not(feature = "regex-lite")
))]
panic!("regex-full or regex-lite feature must be enabled when pattern feature is enabled");

#[cfg(all(feature = "regex-full", feature = "regex-lite"))]
println!("cargo:warning=garde: both regex-full and regex-lite features are enabled, using the regex crate in email and pattern validators");
}
11 changes: 8 additions & 3 deletions garde/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,17 @@
//! |--------------------------|-----------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
//! | `derive` | Enables the usage of the `derive(Validate)` macro | [`garde_derive`](https://crates.io/crates/garde_derive) |
//! | `url` | Validation of URLs via the `url` crate. | [`url`](https://crates.io/crates/url) |
//! | `email` | Validation of emails according to [HTML5](https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address) | [`regex`](https://crates.io/crates/regex), [`once_cell`](https://crates.io/crates/once_cell) |
//! | `email` | Validation of emails according to [HTML5](https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address) | [`regex`](https://crates.io/crates/regex) or [`regex-lite`](https://crates.io/crates/regex-lite), [`once_cell`](https://crates.io/crates/once_cell) |
//! | `email-idna` | Support for [Internationalizing Domain Names for Applications](https://url.spec.whatwg.org/#idna) in email addresses | [`idna`](https://crates.io/crates/idna) |
//! | `pattern` | Validation using regular expressions via the `regex` crate | [`regex`](https://crates.io/crates/regex), [`once_cell`](https://crates.io/crates/once_cell) |
//! | `pattern` | Validation using regular expressions via the `regex` crate | [`regex`](https://crates.io/crates/regex) or [`regex-lite`](https://crates.io/crates/regex-lite), [`once_cell`](https://crates.io/crates/once_cell) |
//! | `credit-card` | Validation of credit card numbers via the `card-validate` crate | [`card-validate`](https://crates.io/crates/card-validate) |
//! | `phone-number` | Validation of phone numbers via the `phonenumber` crate | [`phonenumber`](https://crates.io/crates/phonenumber) |
//! | `regex-lite` | Adds support for the `regex-lite` crate for the pattern validator and switches the email validator to use `regex-lite` instead of `regex` | [`regex-lite`](https://crates.io/crates/regex-lite) |
//! | `regex-full` | Use the `regex` crate in the pattern and email validator | [`regex`](https://crates.io/crates/regex) |
//! | `regex-lite` | Use the `regex-lite` crate in the pattern and email validator | [`regex-lite`](https://crates.io/crates/regex-lite) |
//!
//! Additional notes:
//! - If you enable the `email` or `pattern` feature, you must also enable either `regex-full` or `regex-lite`.
//! - If both `regex-full` and `regex-lite` are enabled, then the `regex-lite` support will be built, but the email validator and the pattern with a string literal will use the `regex` crate, not `regex-lite`.
pub mod error;
pub mod rules;
Expand Down
14 changes: 10 additions & 4 deletions garde/src/rules/pattern.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Pattern validation.
//!
//! The pattern argument can be a regular expression provided as a string literal, which is then parsed by the [`regex`] crate.
//! The pattern argument can be a regular expression provided as a string literal, which is then parsed by the [`regex`] (if the `regex-full` feature is enabled) or [`regex_lite`] crate (if _only_ the `regex-lite` feature is enabled).
//!
//! ```rust
//! #[derive(garde::Validate)]
Expand All @@ -11,7 +11,7 @@
//! ```
//!
//! Alternatively, it can be an expression of type implementing [`IsMatch`] or one that dereferences to a [`IsMatch`].
//! [`IsMatch`] is implemented for `regex::Regex`, `regex_lite::Regex` (if `regex-lite` feature is enabled) and `once_cell::sync::Lazy<T>` with any `T: IsMatch`.
//! [`IsMatch`] is implemented for `regex::Regex` (if the `regex-full` feature is enabled), `regex_lite::Regex` (if the `regex-lite` feature is enabled) and `once_cell::sync::Lazy<T>` with any `T: IsMatch`.
//! Please note that the expression will be evaluated each time `validate` is called, so avoid doing any expensive work in the expression.
//! If the work is unavoidable, at least try to amortize it, such as by using `once_cell::Lazy` or the nightly-only `std::sync::LazyLock`.
//!
Expand All @@ -32,8 +32,12 @@
//!
//! This trait has a blanket implementation for all `T: garde::rules::AsStr`.
#[cfg(feature = "regex-full")]
#[doc(hidden)]
pub use regex::Regex;
#[cfg(all(feature = "regex-lite", not(feature = "regex-full")))]
#[doc(hidden)]
pub use regex_lite::Regex;

use super::AsStr;
use crate::error::Error;
Expand All @@ -53,7 +57,8 @@ pub trait IsMatch: AsStr {
fn is_match(&self, haystack: &str) -> bool;
}

impl IsMatch for Regex {
#[cfg(feature = "regex-full")]
impl IsMatch for regex::Regex {
fn is_match(&self, haystack: &str) -> bool {
self.is_match(haystack)
}
Expand All @@ -72,7 +77,8 @@ impl<T: IsMatch> IsMatch for once_cell::sync::Lazy<T> {
}
}

impl AsStr for Regex {
#[cfg(feature = "regex-full")]
impl AsStr for regex::Regex {
fn as_str(&self) -> &str {
self.as_str()
}
Expand Down
1 change: 1 addition & 0 deletions garde/tests/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod ip;
mod length;
mod multi_rule;
mod option;
#[cfg(any(feature = "regex-full", feature = "regex-lite"))]
mod pattern;
mod phone_number;
mod prefix;
Expand Down
9 changes: 4 additions & 5 deletions garde/tests/rules/pattern.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use once_cell::sync::Lazy;

#[cfg(feature = "regex-full")]
use regex::Regex;
#[cfg(not(feature = "regex-full"))]
use regex_lite::Regex;

use super::util;

mod sub {
use super::*;

#[cfg(feature = "regex-lite")]
pub static LAZY_RE: Lazy<regex_lite::Regex> =
Lazy::new(|| regex_lite::Regex::new(r"^abcd|efgh$").unwrap());

#[cfg(not(feature = "regex-lite"))]
pub static LAZY_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^abcd|efgh$").unwrap());
}

Expand Down
5 changes: 4 additions & 1 deletion garde_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ readme = "../README.md"
proc-macro = true

[features]
default = ["regex"]
default = ["regex-full"]
regex-full = ["dep:regex"]
regex-lite = ["dep:regex-lite"]

[dependencies]
syn = { version = "2", features = ["full"] }
quote = { version = "1" }
proc-macro2 = { version = "1" }
regex = { version = "1", default-features = false, features = ["std"], optional = true }
regex-lite = { version = "0.1", optional = true }
6 changes: 5 additions & 1 deletion garde_derive/src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,10 +373,14 @@ fn check_range_not_ord<T>(range: model::Range<T>) -> syn::Result<model::Validate
fn check_regex(value: model::Pattern) -> syn::Result<model::ValidatePattern> {
match value {
model::Pattern::Lit(lit) => {
#[cfg(feature = "regex")]
#[cfg(feature = "regex-full")]
if let Err(e) = regex::Regex::new(&lit.value) {
return Err(syn::Error::new(lit.span, format!("invalid regex: {e}")));
}
#[cfg(all(feature = "regex-lite", not(feature = "regex-full")))]
if let Err(e) = regex_lite::Regex::new(&lit.value) {
return Err(syn::Error::new(lit.span, format!("invalid regex: {e}")));
}
Ok(model::ValidatePattern::Lit(lit.value))
}
model::Pattern::Expr(expr) => Ok(model::ValidatePattern::Expr(expr)),
Expand Down

0 comments on commit f613346

Please sign in to comment.