Skip to content

Commit

Permalink
Rewrite derive macro implementation (#262)
Browse files Browse the repository at this point in the history
* Rewrite derive macro implementation

* Add better error handling

* Add new types to validator macro

* Add empty files in tokens module

* Removed i32 tests for length

* Fix email to pass tests

* fix length validation trait to work with u64 only

* Add credit card token generation to macro

* Add url token generation to macro

* Add ValidateIp trait

* Add ip token generation to macro

* Remove unneeded import

* Export ValidateIp trait from main crate

* Add tests for ValidateIp macro

* Add non control character token generation to macro

* Add test for range with enums

* Add range token generation to macro

* Add required token generation to macro

* Fix ValidationErrors merge function

* Move contains trait to contains.rs

* Add ValidateContains trait and fix tests

* Add ValidateContains and DoesNotContain traits to macro

* Add must match token generation to macro

* Add regex token generation

* Add custom validation token generation to macro

* Add custom validation with arguments to macro

* Add custom validation with args with contexts

* Add custom validation recommit

* Fix custom validation to work without lifetimes

* Change custom validation to use closures

* Fix tests for custom validation

* Change custom validation to implement FnOnce and add tests

* Remove code used for experementing

* Remove unneeded code

* Remove unused imports

* Add schema with arguments and fix tests

* Impl ValidateLength trait for Option types

* Fix impls for ValidateRange

* Fix duplicate use statements

* Fix tests and add Option impls for Contains

* Implement ValidateUrl traits for specific types

* Implement ValidateEmail traits for specific types

* Fix some tests and warnings

* Add regex trait for validation

* Fix to pass tests in validator lib

* Fix range validation trait to pass tests

* Update and remove unneeded dependencies

* Add ValidateNested trait

* Fix custom and nested validation

* Fix Regex validator to work with OnceCell and OnceLock

* Fix derive macro to support all the arguments

* Remove unneeded functions and fix tests

* Remove validator_types crate and fix tests

* Update CI workflow

* Fix custom to be used in nested validations

* Fix custom validation to use context

* Add custom nested validation with args test

* Fix validation to use Validation trait

* Remove conflicting trait

* Fixed tests and remove ValidateContext trait

* Fix nested validation

* Fix custom args test

* Add `nest_all_fields` attribute

* Add better error handling and start fixing UI tests

* Pass almost all tests

* Add skip_on_field_errors attribute to schema

* Remove rust beta channel from workflow

* Use proc_macro_error instead of darling diagnostics feature

* Conditionally run UI tests

* Fix ci.yml

* Fix ci.yml

* Remove UI test for wrong type on range

* Change trait impls to be consistent

* Run cargo clippy --fix

* Replace if else with match

* Remove cargo-expand leftovers

* Replace underscore with `#[allow(unused)]`

* Add support for multiple schema validations

* Remove serde original field name test
  • Loading branch information
pintariching authored and Keats committed Mar 4, 2024
1 parent df6e5f5 commit 283a00a
Show file tree
Hide file tree
Showing 115 changed files with 3,829 additions and 3,041 deletions.
49 changes: 17 additions & 32 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,28 @@ on: [push, pull_request]

jobs:
test_validator:
name: test validator
runs-on: ${{ matrix.os }}
name: Continuous integration
runs-on: ubuntu-latest
strategy:
matrix:
build: [pinned, stable, nightly]
include:
- build: pinned
os: ubuntu-20.04
rust: 1.56.1
- build: stable
os: ubuntu-20.04
rust: stable
- build: nightly
os: ubuntu-20.04
rust: nightly
- rust: 1.70.0 # MSRV
- rust: stable
- rust: beta
- rust: nightly
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: Install Rust
uses: hecrj/setup-rust-action@v1
uses: dtolnay/rust-toolchain@stable
with:
rust-version: ${{ matrix.rust }}
toolchain: ${{ matrix.rust }}
- name: Cache dependencies
uses: Swatinem/rust-cache@v2
- name: Build System Info
run: rustc --version

- name: tests validator with all features
run: cd validator && cargo test --all-features
- name: tests validator with no features
run: cd validator && cargo test --no-default-features

test_validator_derive:
name: test validator_derive
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v1
- name: Install Rust
uses: hecrj/setup-rust-action@v1
with:
rust-version: stable
- name: tests validator_derive with all features
run: cd validator_derive_tests && cargo test
- name: Tests
run: |
cargo build --no-default-features
cargo test --no-default-features
cargo build ${{ matrix.rust == 'stable' && '--all-features' || '--no-default-features --features card,unic,derive' }}
cargo test ${{ matrix.rust == 'stable' && '--all-features' || '--no-default-features --features card,unic,derive' }}
8 changes: 2 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
[workspace]
members = [
"validator",
"validator_derive",
"validator_types",
"validator_derive_tests"
]
resolver = "2"
members = ["validator", "validator_derive", "validator_derive_tests"]
2 changes: 1 addition & 1 deletion rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
use_small_heuristics = "max"
use_small_heuristics = "Max"
11 changes: 5 additions & 6 deletions validator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description = "Common validation functions (email, url, length, ...) and trait -
homepage = "https://github.com/Keats/validator"
repository = "https://github.com/Keats/validator"
keywords = ["validation", "api", "validator"]
edition = "2018"
edition = "2021"
readme = "../README.md"

[dependencies]
Expand All @@ -19,12 +19,11 @@ serde = "1"
serde_derive = "1"
serde_json = "1"
validator_derive = { version = "0.16", path = "../validator_derive", optional = true }
card-validate = { version = "2.2", optional = true }
card-validate = { version = "2.3", optional = true }
unic-ucd-common = { version = "0.9", optional = true }
indexmap = {version = "1", features = ["serde-1"], optional = true }

indexmap = { version = "2.0.0", features = ["serde"], optional = true }

[features]
card = ["card-validate", "validator_derive/card"]
unic = ["unic-ucd-common", "validator_derive/unic"]
card = ["card-validate"]
unic = ["unic-ucd-common"]
derive = ["validator_derive"]
27 changes: 13 additions & 14 deletions validator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,23 +67,22 @@ mod types;
mod validation;

#[cfg(feature = "card")]
pub use validation::cards::{validate_credit_card, ValidateCreditCard};
pub use validation::contains::validate_contains;
pub use validation::does_not_contain::validate_does_not_contain;
pub use validation::email::{validate_email, ValidateEmail};
pub use validation::ip::{validate_ip, validate_ip_v4, validate_ip_v6};
pub use validation::length::{validate_length, ValidateLength};
pub use validation::cards::ValidateCreditCard;
pub use validation::contains::ValidateContains;
pub use validation::does_not_contain::ValidateDoesNotContain;
pub use validation::email::ValidateEmail;
pub use validation::ip::ValidateIp;
pub use validation::length::ValidateLength;
pub use validation::must_match::validate_must_match;
pub use validation::nested::ValidateNested;
#[cfg(feature = "unic")]
pub use validation::non_control_character::{
validate_non_control_character, ValidateNonControlCharacter,
};
pub use validation::range::{validate_range, ValidateRange};
pub use validation::non_control_character::ValidateNonControlCharacter;
pub use validation::range::ValidateRange;
pub use validation::regex::ValidateRegex;
pub use validation::required::ValidateRequired;
pub use validation::urls::ValidateUrl;

pub use validation::required::{validate_required, ValidateRequired};
pub use validation::urls::{validate_url, ValidateUrl};

pub use traits::{Contains, HasLen, Validate, ValidateArgs};
pub use traits::{HasLen, Validate, ValidateArgs};
pub use types::{ValidationError, ValidationErrors, ValidationErrorsKind};

#[cfg(feature = "derive")]
Expand Down
59 changes: 15 additions & 44 deletions validator/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,49 +144,6 @@ impl<T> HasLen for IndexSet<T> {
}
}

/// Trait to implement if one wants to make the `contains` validator
/// work for more types
pub trait Contains {
#[must_use]
fn has_element(&self, needle: &str) -> bool;
}

impl Contains for String {
fn has_element(&self, needle: &str) -> bool {
self.contains(needle)
}
}

impl<'a> Contains for &'a String {
fn has_element(&self, needle: &str) -> bool {
self.contains(needle)
}
}

impl<'a> Contains for &'a str {
fn has_element(&self, needle: &str) -> bool {
self.contains(needle)
}
}

impl<'a> Contains for Cow<'a, str> {
fn has_element(&self, needle: &str) -> bool {
self.contains(needle)
}
}

impl<S, H: ::std::hash::BuildHasher> Contains for HashMap<String, S, H> {
fn has_element(&self, needle: &str) -> bool {
self.contains_key(needle)
}
}

impl<'a, S, H: ::std::hash::BuildHasher> Contains for &'a HashMap<String, S, H> {
fn has_element(&self, needle: &str) -> bool {
self.contains_key(needle)
}
}

/// This is the original trait that was implemented by deriving `Validate`. It will still be
/// implemented for struct validations that don't take custom arguments. The call is being
/// forwarded to the `ValidateArgs<'v_a>` trait.
Expand All @@ -207,6 +164,20 @@ impl<T: Validate> Validate for &T {
/// The `Args` type can use the lifetime `'v_a` to pass references onto the validator.
pub trait ValidateArgs<'v_a> {
type Args;
fn validate_with_args(&self, args: Self::Args) -> Result<(), ValidationErrors>;
}

impl<'v_a, T, U> ValidateArgs<'v_a> for Option<T>
where
T: ValidateArgs<'v_a, Args = U>,
{
type Args = U;

fn validate_args(&self, args: Self::Args) -> Result<(), ValidationErrors>;
fn validate_with_args(&self, args: Self::Args) -> Result<(), ValidationErrors> {
if let Some(nested) = self {
T::validate_with_args(nested, args)
} else {
Ok(())
}
}
}
18 changes: 17 additions & 1 deletion validator/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub enum ValidationErrorsKind {
}

#[derive(Default, Debug, Serialize, Clone, PartialEq)]
pub struct ValidationErrors(HashMap<&'static str, ValidationErrorsKind>);
pub struct ValidationErrors(pub HashMap<&'static str, ValidationErrorsKind>);

impl ValidationErrors {
pub fn new() -> ValidationErrors {
Expand All @@ -58,6 +58,22 @@ impl ValidationErrors {
}
}

pub fn merge_self(
&mut self,
field: &'static str,
child: Result<(), ValidationErrors>,
) -> &mut ValidationErrors {
match child {
Ok(()) => self,
Err(errors) => {
for (_, e) in errors.0 {
self.add_nested(field, e);
}
self
}
}
}

/// Returns the combined outcome of a struct's validation result along with the nested
/// validation result for one of its fields.
pub fn merge(
Expand Down
28 changes: 11 additions & 17 deletions validator/src/validation/cards.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,29 @@
use std::borrow::Cow;

#[cfg(feature = "card")]
use card_validate::Validate as CardValidate;

#[must_use]
pub fn validate_credit_card<T: ValidateCreditCard>(card: T) -> bool
{
card.validate_credit_card()
}

pub trait ValidateCreditCard {
#[must_use]
fn validate_credit_card(&self) -> bool {
let card_string = self.to_credit_card_string();
let card_string = self.as_credit_card_string();
CardValidate::from(&card_string).is_ok()
}

fn to_credit_card_string(&self) -> Cow<str>;
fn as_credit_card_string(&self) -> Cow<str>;
}

impl<T: AsRef<str>> ValidateCreditCard for T {
fn to_credit_card_string(&self) -> Cow<str> {
fn as_credit_card_string(&self) -> Cow<str> {
Cow::from(self.as_ref())
}
}

#[cfg(test)]
#[cfg(feature = "card")]
mod tests {
use super::ValidateCreditCard;
use std::borrow::Cow;

use super::validate_credit_card;

#[test]
fn test_credit_card() {
let tests = vec![
Expand All @@ -40,19 +34,19 @@ mod tests {
];

for (input, expected) in tests {
assert_eq!(validate_credit_card(input), expected);
assert_eq!(input.validate_credit_card(), expected);
}
}

#[test]
fn test_credit_card_cow() {
let test: Cow<'static, str> = "4539571147647251".into();
assert!(validate_credit_card(test));
assert!(test.validate_credit_card());
let test: Cow<'static, str> = String::from("4539571147647251").into();
assert!(validate_credit_card(test));
assert!(test.validate_credit_card());
let test: Cow<'static, str> = "5236313877109141".into();
assert!(!validate_credit_card(test));
assert!(!test.validate_credit_card());
let test: Cow<'static, str> = String::from("5236313877109141").into();
assert!(!validate_credit_card(test));
assert!(!test.validate_credit_card());
}
}
Loading

0 comments on commit 283a00a

Please sign in to comment.