Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions crates/serde_valid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,34 @@ where
}
}

impl<T> Validate for std::rc::Rc<T>
where
T: Validate + ?Sized,
{
fn validate(&self) -> std::result::Result<(), self::validation::Errors> {
self.as_ref().validate()
}
}

impl<T> Validate for std::sync::Arc<T>
where
T: Validate + ?Sized,
{
fn validate(&self) -> std::result::Result<(), self::validation::Errors> {
self.as_ref().validate()
}
}

impl<P> Validate for std::pin::Pin<P>
where
P: std::ops::Deref,
P::Target: Validate,
{
fn validate(&self) -> std::result::Result<(), self::validation::Errors> {
self.as_ref().get_ref().validate()
}
}

impl<T> Validate for Vec<T>
where
T: Validate,
Expand Down
18 changes: 18 additions & 0 deletions crates/serde_valid/src/traits/is_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@ where
}
}

impl<T> IsMatch for std::rc::Rc<T>
where
T: IsMatch + ?Sized,
{
fn is_match(&self, pattern: &regex::Regex) -> bool {
self.as_ref().is_match(pattern)
}
}

impl<T> IsMatch for std::sync::Arc<T>
where
T: IsMatch + ?Sized,
{
fn is_match(&self, pattern: &regex::Regex) -> bool {
self.as_ref().is_match(pattern)
}
}

macro_rules! impl_for_str {
($ty:ty) => {
impl IsMatch for $ty {
Expand Down
18 changes: 18 additions & 0 deletions crates/serde_valid/src/traits/length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,24 @@ where
}
}

impl<T> Length for std::rc::Rc<T>
where
T: Length + ?Sized,
{
fn length(&self) -> usize {
self.as_ref().length()
}
}

impl<T> Length for std::sync::Arc<T>
where
T: Length + ?Sized,
{
fn length(&self) -> usize {
self.as_ref().length()
}
}

macro_rules! impl_for_str {
($ty:ty) => {
impl Length for $ty {
Expand Down
18 changes: 18 additions & 0 deletions crates/serde_valid/src/traits/size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ pub trait Size {
fn size(&self) -> usize;
}

impl<T> Size for std::rc::Rc<T>
where
T: Size + ?Sized,
{
fn size(&self) -> usize {
self.as_ref().size()
}
}

impl<T> Size for std::sync::Arc<T>
where
T: Size + ?Sized,
{
fn size(&self) -> usize {
self.as_ref().size()
}
}

impl<K, V> Size for HashMap<K, V> {
fn size(&self) -> usize {
self.len()
Expand Down
9 changes: 9 additions & 0 deletions crates/serde_valid/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ macro_rules! for_each_composited_wrapper {
$callback!([$($context)*] [T, const N: usize] Box<[T; N]> => [T; N]; []);
$callback!([$($context)*] [T] &Vec<T> => Vec<T>; []);
$callback!([$($context)*] [T] Box<Vec<T>> => Vec<T>; []);
$callback!([$($context)*] [T] Box<Box<Vec<T>>> => Box<Vec<T>>; []);
$callback!([$($context)*] ['a, T] &'a Box<Vec<T>> => Box<Vec<T>>; []);
$callback!([$($context)*] ['a, T] Box<&'a Vec<T>> => &'a Vec<T>; []);
$callback!([$($context)*] [T] std::rc::Rc<Vec<T>> => Vec<T>; []);
$callback!([$($context)*] [T] std::sync::Arc<Vec<T>> => Vec<T>; []);
$callback!([$($context)*] [T] std::pin::Pin<Box<Vec<T>>> => Vec<T>; []);
$callback!([$($context)*] [T] &Option<T> => Option<T>; []);
$callback!([$($context)*] [T] Box<Option<T>> => Option<T>; []);
$callback!([
Expand Down Expand Up @@ -119,6 +125,9 @@ macro_rules! for_each_composited_wrapper {
$callback!([
$($context)*
] ['a, T] std::borrow::Cow<'a, [T]> => [T]; [[T]: std::borrow::ToOwned]);
$callback!([
$($context)*
] ['a, T] Box<std::borrow::Cow<'a, [T]>> => std::borrow::Cow<'a, [T]>; [[T]: std::borrow::ToOwned]);
};
}

Expand Down
28 changes: 28 additions & 0 deletions crates/serde_valid/src/validation/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,34 @@ macro_rules! impl_validate_array_length_items {
}
}

impl<T> $ValidateTrait for std::rc::Rc<T>
where
T: $ValidateTrait + ?Sized,
{
fn $validate_method(&self, limit: usize) -> Result<(), $Error> {
self.as_ref().$validate_method(limit)
}
}

impl<T> $ValidateTrait for std::sync::Arc<T>
where
T: $ValidateTrait + ?Sized,
{
fn $validate_method(&self, limit: usize) -> Result<(), $Error> {
self.as_ref().$validate_method(limit)
}
}

impl<P> $ValidateTrait for std::pin::Pin<P>
where
P: std::ops::Deref,
P::Target: $ValidateTrait,
{
fn $validate_method(&self, limit: usize) -> Result<(), $Error> {
self.as_ref().get_ref().$validate_method(limit)
}
}

impl<T> $ValidateTrait for Option<T>
where
T: $ValidateTrait,
Expand Down
28 changes: 28 additions & 0 deletions crates/serde_valid/src/validation/array/unique_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,34 @@ where
}
}

impl<T> ValidateUniqueItems for std::rc::Rc<T>
where
T: ValidateUniqueItems + ?Sized,
{
fn validate_unique_items(&self) -> Result<(), crate::UniqueItemsError> {
self.as_ref().validate_unique_items()
}
}

impl<T> ValidateUniqueItems for std::sync::Arc<T>
where
T: ValidateUniqueItems + ?Sized,
{
fn validate_unique_items(&self) -> Result<(), crate::UniqueItemsError> {
self.as_ref().validate_unique_items()
}
}

impl<P> ValidateUniqueItems for std::pin::Pin<P>
where
P: std::ops::Deref,
P::Target: ValidateUniqueItems,
{
fn validate_unique_items(&self) -> Result<(), crate::UniqueItemsError> {
self.as_ref().get_ref().validate_unique_items()
}
}

impl<T> ValidateUniqueItems for Vec<T>
where
T: std::cmp::Eq + std::hash::Hash + std::fmt::Debug,
Expand Down
18 changes: 18 additions & 0 deletions crates/serde_valid/src/validation/generic/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,24 @@ where
}
}

impl<C, T> ValidateEnum<C> for std::rc::Rc<T>
where
T: ValidateEnum<C> + ?Sized,
{
fn validate_enum(&self, candidates: &[C]) -> Result<(), EnumError> {
self.as_ref().validate_enum(candidates)
}
}

impl<C, T> ValidateEnum<C> for std::sync::Arc<T>
where
T: ValidateEnum<C> + ?Sized,
{
fn validate_enum(&self, candidates: &[C]) -> Result<(), EnumError> {
self.as_ref().validate_enum(candidates)
}
}

macro_rules! impl_validate_generic_enumerate_path {
($type:ty) => {
impl ValidateEnum<&'static str> for $type {
Expand Down
18 changes: 18 additions & 0 deletions crates/serde_valid/src/validation/generic/enumerate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,24 @@ where
}
}

impl<C, T> ValidateEnumerate<C> for std::rc::Rc<T>
where
T: ValidateEnumerate<C> + ?Sized,
{
fn validate_enumerate(&self, enumerate: &[C]) -> Result<(), EnumError> {
self.as_ref().validate_enumerate(enumerate)
}
}

impl<C, T> ValidateEnumerate<C> for std::sync::Arc<T>
where
T: ValidateEnumerate<C> + ?Sized,
{
fn validate_enumerate(&self, enumerate: &[C]) -> Result<(), EnumError> {
self.as_ref().validate_enumerate(enumerate)
}
}

macro_rules! impl_validate_generic_enumerate_path {
($type:ty) => {
impl ValidateEnumerate<&'static str> for $type {
Expand Down
24 changes: 24 additions & 0 deletions crates/serde_valid/tests/derive_enum_unit_validation_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use serde_valid::Validate;

fn always_invalid(_value: &Input) -> Result<(), serde_valid::validation::Error> {
Err(serde_valid::validation::Error::Custom(
"invalid enum".to_owned(),
))
}

#[derive(Validate)]
#[validate(custom = always_invalid)]
enum Input {
Unit,
Tuple(u8),
}

#[test]
fn enum_level_custom_validation_runs_for_unit_variants() {
assert!(Input::Unit.validate().is_err());
}

#[test]
fn enum_level_custom_validation_still_runs_for_data_variants() {
assert!(Input::Tuple(0).validate().is_err());
}
45 changes: 45 additions & 0 deletions crates/serde_valid/tests/derive_generated_binding_hygiene_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#![allow(dead_code, non_upper_case_globals)]

use serde_valid::Validate;

static field: () = ();
static errors: () = ();
static error: () = ();
static other: () = ();
static index: () = ();
static __field_errors: () = ();
static __array_errors: () = ();
static __object_errors: () = ();
static error_params: &str = "custom minimum";
static value: () = ();
static __0: () = ();

#[derive(Validate)]
struct Input {
value: String,
}

#[derive(Validate)]
struct TupleInput(#[validate(min_length = 1)] String);

#[derive(Validate)]
struct InputWithMessage {
#[validate(min_items = 1, message_fn = |_| error_params.to_owned())]
values: Vec<u8>,
}

#[test]
fn generated_error_bindings_do_not_collide_with_user_statics() {
assert!(Input {
value: String::new(),
}
.validate()
.is_ok());
assert!(TupleInput("value".to_owned()).validate().is_ok());
}

#[test]
fn custom_message_expressions_resolve_user_statics() {
let validation_errors = InputWithMessage { values: vec![] }.validate().unwrap_err();
assert!(validation_errors.to_string().contains(error_params));
}
Loading