[][src]Trait domain_patterns::models::ValueObject

pub trait ValueObject<T>: Clone + PartialEq + TryFrom<T> + Display {
    type ValueError;
    fn validate(value: &T) -> Result<(), Self::ValueError>;
fn value(&self) -> T; }

A trait that defines a ValueObject which is an immutable holder of value, that validates that value against certain conditions before storing it.

Example

use std::{fmt, error};
use std::convert::TryFrom;
use regex::Regex;
use domain_patterns::models::ValueObject;


#[derive(Clone, PartialEq)]
struct Email {
    value: String,
}

#[derive(Debug, Clone)]
struct EmailValidationError;

impl fmt::Display for EmailValidationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Email failed to validate.")
    }
}

impl error::Error for EmailValidationError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        None
    }
}

impl TryFrom<String> for Email {
    type Error = EmailValidationError;

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

        Ok(Email {
            value,
        })
    }
}

impl ValueObject<String> for Email {
    type ValueError = EmailValidationError;

    fn validate(value: &String) -> Result<(), EmailValidationError> {
        let email_rx = Regex::new(
            r"^(?i)[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$"
        ).unwrap();

        if !email_rx.is_match(value) {
            return Err(EmailValidationError);
        }

        Ok(())
    }

    fn value(&self) -> String {
        return self.value.clone()
    }
}

impl fmt::Display for Email {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.value)
    }
}

let email = Email::try_from("test_email@email.com".to_string()).unwrap();

Associated Types

type ValueError

ValueError defines an error type that communicates there was a problem with validation.

Loading content...

Required methods

fn validate(value: &T) -> Result<(), Self::ValueError>

validate takes in incoming data used to construct the value object, and validates it against given constraints. An example would be if we had an Email struct that implements ValueObject. The constraints we would check would ensure that the incoming data is a valid email address.

Note: validate should be called by your implementation of try_from.

fn value(&self) -> T

value return a reference to the internal value held in the value object. This should be the only way that we access the internal data. Mutation methods should always generate a new value object. Note: It's intentional that value returns an owned type. This is necessary for enums, where we likely want to return a String after matching (since a string is how we match to figure out the variant upon value object creation), but in that case the string is created on the match in value(), and therefore we must pass back an owned value, not a ref (the string that was freshly created would be dropped at the end of value() if we try to pass back a ref of it).

Loading content...

Implementors

Loading content...