mod aliases;
mod combinators;
mod effect;
pub mod predicates;
#[cfg(feature = "serde")]
mod serde_impl;
mod validation;
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
pub use aliases::*;
pub use combinators::{And, AndError, Not, NotError, Or, OrError};
pub use effect::{pure_refined, refine};
pub use predicates::collection::{MaxSize, MinSize};
pub use predicates::numeric::{InRange, Negative, NonNegative, NonZero, Positive};
pub use predicates::string::{MaxLength, MinLength, NonEmpty, Trimmed};
pub use validation::{FieldError, RefinedValidationExt, ValidationFieldExt};
pub trait Predicate<T>: Send + Sync + 'static {
type Error: Send + Sync;
fn check(value: &T) -> Result<(), Self::Error>;
fn description() -> &'static str {
std::any::type_name::<Self>()
}
}
pub struct Refined<T, P: Predicate<T>> {
value: T,
_predicate: PhantomData<P>,
}
impl<T, P: Predicate<T>> Refined<T, P> {
pub fn new(value: T) -> Result<Self, P::Error> {
P::check(&value)?;
Ok(Self {
value,
_predicate: PhantomData,
})
}
#[inline]
pub fn get(&self) -> &T {
&self.value
}
#[inline]
pub fn into_inner(self) -> T {
self.value
}
#[inline]
pub fn new_unchecked(value: T) -> Self {
Self {
value,
_predicate: PhantomData,
}
}
pub fn try_map<F>(self, f: F) -> Result<Self, P::Error>
where
F: FnOnce(T) -> T,
{
Self::new(f(self.value))
}
}
impl<T: fmt::Debug, P: Predicate<T>> fmt::Debug for Refined<T, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Refined")
.field("value", &self.value)
.field("predicate", &std::any::type_name::<P>())
.finish()
}
}
impl<T: Clone, P: Predicate<T>> Clone for Refined<T, P> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
_predicate: PhantomData,
}
}
}
impl<T: PartialEq, P: Predicate<T>> PartialEq for Refined<T, P> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<T: Eq, P: Predicate<T>> Eq for Refined<T, P> {}
impl<T: PartialOrd, P: Predicate<T>> PartialOrd for Refined<T, P> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.value.partial_cmp(&other.value)
}
}
impl<T: Ord, P: Predicate<T>> Ord for Refined<T, P> {
fn cmp(&self, other: &Self) -> Ordering {
self.value.cmp(&other.value)
}
}
impl<T: Hash, P: Predicate<T>> Hash for Refined<T, P> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}
impl<T, P: Predicate<T>> AsRef<T> for Refined<T, P> {
fn as_ref(&self) -> &T {
&self.value
}
}
impl<T, P: Predicate<T>> std::ops::Deref for Refined<T, P> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
impl<T: fmt::Display, P: Predicate<T>> fmt::Display for Refined<T, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.value.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Even;
impl Predicate<i32> for Even {
type Error = &'static str;
fn check(value: &i32) -> Result<(), Self::Error> {
if value % 2 == 0 {
Ok(())
} else {
Err("value must be even")
}
}
}
type EvenI32 = Refined<i32, Even>;
#[test]
fn test_new_success() {
let result = EvenI32::new(42);
assert!(result.is_ok());
assert_eq!(*result.unwrap().get(), 42);
}
#[test]
fn test_new_failure() {
let result = EvenI32::new(41);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), "value must be even");
}
#[test]
fn test_get() {
let n = EvenI32::new(42).unwrap();
assert_eq!(*n.get(), 42);
}
#[test]
fn test_into_inner() {
let n = EvenI32::new(42).unwrap();
assert_eq!(n.into_inner(), 42);
}
#[test]
fn test_new_unchecked() {
let n = EvenI32::new_unchecked(41);
assert_eq!(*n.get(), 41);
}
#[test]
fn test_try_map_success() {
let n = EvenI32::new(42).unwrap();
let doubled = n.try_map(|x| x * 2);
assert!(doubled.is_ok());
assert_eq!(*doubled.unwrap().get(), 84);
}
#[test]
fn test_try_map_failure() {
let n = EvenI32::new(42).unwrap();
let odd = n.try_map(|x| x + 1);
assert!(odd.is_err());
}
#[test]
fn test_clone() {
let n = EvenI32::new(42).unwrap();
let cloned = n.clone();
assert_eq!(*n.get(), *cloned.get());
}
#[test]
fn test_partial_eq() {
let a = EvenI32::new(42).unwrap();
let b = EvenI32::new(42).unwrap();
let c = EvenI32::new(44).unwrap();
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn test_ord() {
let a = EvenI32::new(42).unwrap();
let b = EvenI32::new(44).unwrap();
assert!(a < b);
assert!(b > a);
}
#[test]
fn test_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(EvenI32::new(42).unwrap());
set.insert(EvenI32::new(42).unwrap()); set.insert(EvenI32::new(44).unwrap());
assert_eq!(set.len(), 2);
}
#[test]
fn test_as_ref() {
let n = EvenI32::new(42).unwrap();
let r: &i32 = n.as_ref();
assert_eq!(*r, 42);
}
#[test]
fn test_deref() {
let n = EvenI32::new(42).unwrap();
assert_eq!(*n, 42);
}
#[test]
fn test_display() {
let n = EvenI32::new(42).unwrap();
assert_eq!(format!("{}", n), "42");
}
#[test]
fn test_debug() {
let n = EvenI32::new(42).unwrap();
let debug = format!("{:?}", n);
assert!(debug.contains("Refined"));
assert!(debug.contains("42"));
}
}