[−][src]Trait domain_patterns::models::ValueObject
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.
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).