#![deny(rustdoc::broken_intra_doc_links)]
pub use into_inner::IntoInner;
use duplicate::duplicate_item;
use num::Complex;
use std::convert::Infallible;
pub trait TryNew: Sized + IntoInner {
#[cfg(feature = "std")]
type Error: std::error::Error;
#[cfg(not(feature = "std"))]
type Error: core::fmt::Debug;
fn try_new(value: Self::InnerType) -> Result<Self, Self::Error>;
}
pub trait New: Sized + IntoInner {
fn new(value: Self::InnerType) -> Self;
}
pub trait ConditionallyCreate: Sized + TryNew + New
where
<Self as TryNew>::Error: core::fmt::Debug,
{
fn create_conditionally(value: Self::InnerType) -> Self {
#[cfg(debug_assertions)]
{
Self::try_new(value).expect("ConditionallyCreate: try_new() failed in debug mode")
}
#[cfg(not(debug_assertions))]
{
Self::new(value)
}
}
}
impl<T> ConditionallyCreate for T
where
T: TryNew + New,
<T as TryNew>::Error: core::fmt::Debug, {
}
pub trait ValidationPolicy {
type Value;
#[cfg(feature = "std")]
type Error: std::error::Error;
#[cfg(not(feature = "std"))]
type Error: core::fmt::Debug;
fn validate(v: Self::Value) -> Result<Self::Value, Self::Error> {
Self::validate_ref(&v)?;
Ok(v)
}
fn validate_ref(v: &Self::Value) -> Result<(), Self::Error>;
}
pub trait TryNewValidated: TryNew {
type Policy: ValidationPolicy<Value = <Self as IntoInner>::InnerType>;
fn try_new_validated(
value: <Self as IntoInner>::InnerType,
) -> Result<Self, Self::Error>
where
Self: Sized;
}
#[duplicate_item(
T;
[f32];
[f64];
[i8];
[i16];
[i32];
[i64];
[i128];
[u8];
[u16];
[u32];
[u64];
[u128];
[isize];
[usize];
[bool];
[char];
[Complex<f32>];
[Complex<f64>];
)]
impl New for T {
fn new(value: Self::InnerType) -> Self {
value
}
}
#[duplicate_item(
T;
[f32];
[f64];
[i8];
[i16];
[i32];
[i64];
[i128];
[u8];
[u16];
[u32];
[u64];
[u128];
[isize];
[usize];
[bool];
[char];
[Complex<f32>];
[Complex<f64>];
)]
impl TryNew for T {
type Error = Infallible;
fn try_new(value: Self::InnerType) -> Result<Self, Infallible> {
Ok(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq)]
struct TestNotPositiveError;
#[cfg(feature = "std")]
impl std::fmt::Display for TestNotPositiveError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Value must be positive (Test Error)")
}
}
#[cfg(feature = "std")]
impl std::error::Error for TestNotPositiveError {}
#[derive(Debug, PartialEq)]
struct TestPositiveInteger {
value: i32,
}
impl IntoInner for TestPositiveInteger {
type InnerType = i32;
fn into_inner(self) -> Self::InnerType {
self.value
}
}
impl TryNew for TestPositiveInteger {
type Error = TestNotPositiveError;
fn try_new(value: Self::InnerType) -> Result<Self, Self::Error> {
if value > 0 {
Ok(TestPositiveInteger { value })
} else {
Err(TestNotPositiveError)
}
}
}
impl New for TestPositiveInteger {
fn new(value: Self::InnerType) -> Self {
match Self::try_new(value) {
Ok(instance) => instance,
Err(_) => panic!("TestPositiveInteger::new: Value must be positive."),
}
}
}
mod try_new {
use super::*;
#[test]
fn test_positive_integer_try_new_ok() {
assert_eq!(
TestPositiveInteger::try_new(10),
Ok(TestPositiveInteger { value: 10 })
);
}
#[test]
fn test_positive_integer_try_new_err_zero() {
assert_eq!(TestPositiveInteger::try_new(0), Err(TestNotPositiveError));
}
#[test]
fn test_positive_integer_try_new_err_negative() {
assert_eq!(TestPositiveInteger::try_new(-5), Err(TestNotPositiveError));
}
#[test]
fn test_my_type_try_new_ok() {
assert_eq!(TestMyType::try_new(5), Ok(TestMyType(5)));
}
#[test]
fn test_my_type_try_new_err() {
assert_eq!(
TestMyType::try_new(-1),
Err(TestMyTypeError("Value cannot be negative".to_string()))
);
}
}
mod new {
use super::*;
#[test]
fn test_positive_integer_new_ok() {
assert_eq!(
TestPositiveInteger::new(10),
TestPositiveInteger { value: 10 }
);
}
#[test]
#[should_panic(expected = "TestPositiveInteger::new: Value must be positive.")]
fn test_positive_integer_new_panic_zero() {
TestPositiveInteger::new(0);
}
#[test]
#[should_panic(expected = "TestPositiveInteger::new: Value must be positive.")]
fn test_positive_integer_new_panic_negative() {
TestPositiveInteger::new(-10);
}
#[test]
fn test_my_type_new_ok() {
assert_eq!(TestMyType::new(100), TestMyType(100));
}
#[test]
#[should_panic(expected = "TestMyType::new: Value cannot be negative")]
fn test_my_type_new_panic() {
TestMyType::new(-1);
}
}
mod into_inner {
use super::*;
#[test]
fn test_positive_integer_into_inner() {
let pi = TestPositiveInteger::new(42);
assert_eq!(pi.into_inner(), 42);
}
#[test]
fn test_my_type_into_inner() {
let mt = TestMyType::new(7);
assert_eq!(mt.into_inner(), 7);
}
}
#[derive(Debug, PartialEq)]
struct TestMyTypeError(String);
#[cfg(feature = "std")]
impl std::fmt::Display for TestMyTypeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TestMyTypeError: {}", self.0)
}
}
#[cfg(feature = "std")]
impl std::error::Error for TestMyTypeError {}
#[derive(Debug, PartialEq)]
struct TestMyType(i32);
impl IntoInner for TestMyType {
type InnerType = i32;
fn into_inner(self) -> Self::InnerType {
self.0
}
}
impl TryNew for TestMyType {
type Error = TestMyTypeError;
fn try_new(value: i32) -> Result<Self, Self::Error> {
if value < 0 {
Err(TestMyTypeError("Value cannot be negative".to_string()))
} else {
Ok(TestMyType(value))
}
}
}
impl New for TestMyType {
fn new(value: i32) -> Self {
if value < 0 {
panic!("TestMyType::new: Value cannot be negative");
}
TestMyType(value)
}
}
mod conditionally_create {
use super::*;
#[test]
#[cfg(debug_assertions)] fn test_positive_integer_conditionally_create_debug_ok() {
assert_eq!(
TestPositiveInteger::create_conditionally(10),
TestPositiveInteger { value: 10 }
);
}
#[test]
#[cfg(debug_assertions)] #[should_panic(expected = "ConditionallyCreate: try_new() failed in debug mode")]
fn test_positive_integer_conditionally_create_debug_panic() {
TestPositiveInteger::create_conditionally(-5);
}
#[test]
#[cfg(not(debug_assertions))] fn test_positive_integer_conditionally_create_release_ok() {
assert_eq!(
TestPositiveInteger::create_conditionally(10),
TestPositiveInteger { value: 10 }
);
}
#[test]
#[cfg(not(debug_assertions))] #[should_panic(expected = "TestPositiveInteger::new: Value must be positive.")]
fn test_positive_integer_conditionally_create_release_panic() {
TestPositiveInteger::create_conditionally(-5);
}
#[test]
#[cfg(debug_assertions)]
fn test_my_type_conditionally_create_debug_ok() {
assert_eq!(TestMyType::create_conditionally(20), TestMyType(20));
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "ConditionallyCreate: try_new() failed in debug mode")]
fn test_my_type_conditionally_create_debug_panic() {
TestMyType::create_conditionally(-3);
}
#[test]
#[cfg(not(debug_assertions))]
fn test_my_type_conditionally_create_release_ok() {
assert_eq!(TestMyType::create_conditionally(20), TestMyType(20));
}
#[test]
#[cfg(not(debug_assertions))]
#[should_panic(expected = "TestMyType::new: Value cannot be negative")]
fn test_my_type_conditionally_create_release_panic() {
TestMyType::create_conditionally(-3);
}
}
mod try_new_validated {
use super::*;
#[derive(Debug, PartialEq)]
struct TestPolicyError(String);
#[cfg(feature = "std")]
impl std::fmt::Display for TestPolicyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TestPolicyError: {}", self.0)
}
}
#[cfg(feature = "std")]
impl std::error::Error for TestPolicyError {}
struct MinValuePolicy {
min_value: i32,
}
impl ValidationPolicy for MinValuePolicy {
type Value = i32;
type Error = TestPolicyError;
fn validate_ref(v: &Self::Value) -> Result<(), Self::Error> {
if *v >= 0 {
Ok(())
} else {
Err(TestPolicyError("Value must be non-negative.".to_string()))
}
}
}
#[derive(Debug, PartialEq)]
enum TestValidatedTypeError {
Policy(TestPolicyError),
Construction(String),
}
#[cfg(feature = "std")]
impl std::fmt::Display for TestValidatedTypeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TestValidatedTypeError::Policy(e) => write!(f, "Policy error: {}", e),
TestValidatedTypeError::Construction(s) => {
write!(f, "Construction error: {}", s)
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for TestValidatedTypeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
TestValidatedTypeError::Policy(e) => Some(e),
TestValidatedTypeError::Construction(_) => None,
}
}
}
impl From<TestPolicyError> for TestValidatedTypeError {
fn from(e: TestPolicyError) -> Self {
TestValidatedTypeError::Policy(e)
}
}
#[derive(Debug, PartialEq)]
struct MyValidatedType {
value: i32,
}
impl IntoInner for MyValidatedType {
type InnerType = i32;
fn into_inner(self) -> Self::InnerType {
self.value
}
}
impl TryNew for MyValidatedType {
type Error = TestValidatedTypeError;
fn try_new(value: Self::InnerType) -> Result<Self, Self::Error> {
if value > 10 {
Ok(MyValidatedType { value })
} else {
Err(TestValidatedTypeError::Construction(
"Value must be greater than 10 for construction.".to_string(),
))
}
}
}
impl TryNewValidated for MyValidatedType {
type Policy = MinValuePolicy;
fn try_new_validated(value: <Self as IntoInner>::InnerType) -> Result<Self, Self::Error>
where
Self: Sized,
Self::Error: From<<Self::Policy as ValidationPolicy>::Error>,
{
Self::Policy::validate_ref(&value)?;
Self::try_new(value)
}
}
#[test]
fn validation_policy_validate_ref_ok() {
assert_eq!(MinValuePolicy::validate_ref(&5), Ok(()));
assert_eq!(MinValuePolicy::validate_ref(&0), Ok(()));
}
#[test]
fn validation_policy_validate_ref_err() {
assert_eq!(
MinValuePolicy::validate_ref(&-5),
Err(TestPolicyError("Value must be non-negative.".to_string()))
);
}
#[test]
fn validation_policy_validate_consuming_ok() {
assert_eq!(MinValuePolicy::validate(5), Ok(5));
assert_eq!(MinValuePolicy::validate(0), Ok(0));
}
#[test]
fn validation_policy_validate_consuming_err() {
assert_eq!(
MinValuePolicy::validate(-5),
Err(TestPolicyError("Value must be non-negative.".to_string()))
);
}
#[test]
fn try_new_validated_success() {
assert_eq!(
MyValidatedType::try_new_validated(15),
Ok(MyValidatedType { value: 15 })
);
}
#[test]
fn try_new_validated_policy_fail() {
let result = MyValidatedType::try_new_validated(-5);
assert!(matches!(result, Err(TestValidatedTypeError::Policy(..))));
}
#[test]
fn try_new_validated_construction_fail_after_policy_pass() {
let result = MyValidatedType::try_new_validated(5);
assert!(matches!(
result,
Err(TestValidatedTypeError::Construction(..))
));
}
#[test]
fn try_new_validated_policy_pass_construction_pass_edge() {
assert_eq!(
MyValidatedType::try_new_validated(11),
Ok(MyValidatedType { value: 11 })
);
}
#[test]
fn try_new_validated_policy_pass_construction_fail_edge() {
let result = MyValidatedType::try_new_validated(10);
assert!(matches!(
result,
Err(TestValidatedTypeError::Construction(..))
));
}
}
}