typed-value 0.1.0

A type-safe, validatable value object.
Documentation
extern crate typed_value;

use typed_value::*;

const ALPHA_NUMERIC_REGEX: once_cell::sync::Lazy<regex::Regex> =
    once_cell::sync::Lazy::new(|| regex::Regex::new("^[A-Za-z0-9]*$").expect("invalid regex"));

enum AlphaNumericProperty {}

impl Property for AlphaNumericProperty {
    type Value = String;
    type Error = Box<dyn std::error::Error>;

    fn validate(value: &Self::Value) -> Result<(), Self::Error> {
        if ALPHA_NUMERIC_REGEX.is_match(value) {
            Ok(())
        } else {
            Err(anyhow::anyhow!("{} is not alpha numeric", value).into())
        }
    }
}

struct FixedLengthProperty<T, const N: usize>(T);

impl<T, const N: usize> Property for FixedLengthProperty<T, N>
where
    T: Property<Value = String, Error = Box<dyn std::error::Error>>,
{
    type Value = String;
    type Error = Box<dyn std::error::Error>;

    fn validate(value: &Self::Value) -> Result<(), Self::Error> {
        T::validate(value)?;

        if value.chars().count() == N {
            Ok(())
        } else {
            Err(anyhow::anyhow!("{} length is not equal: {}", value, N).into())
        }
    }
}

type FixedLengthAlphaNumeric<const N: usize> =
    TypedValue<FixedLengthProperty<AlphaNumericProperty, N>>;

#[test]
fn ok_when_initialized_with_alpha_numeric_string() {
    assert!(FixedLengthAlphaNumeric::<16>::new("0123456789ABCDEF".into()).is_ok());
}

#[test]
fn err_when_initialized_with_alpha_numeric_string() {
    assert!(FixedLengthAlphaNumeric::<1>::new("".into()).is_err());
    assert!(FixedLengthAlphaNumeric::<1>::new("12".into()).is_err());
}