ddd_toolkit_core/domain/
vo.rs1use std::fmt::Debug;
2
3pub trait ValueObject: Clone + PartialEq + Debug + Send + Sync {}
6
7pub trait WrappedInner {
9 type Inner;
11}
12
13pub trait Wrapped:
20 ValueObject
21 + WrappedInner
22 + TryFrom<<Self as WrappedInner>::Inner, Error: std::error::Error + Send + Sync + 'static>
23{
24 fn as_inner(&self) -> &Self::Inner;
26 fn into_inner(self) -> Self::Inner;
28}
29
30#[cfg(test)]
31mod test {
32 use super::*;
33 use crate::domain::ValidationError;
34
35 #[derive(Clone, PartialEq, Debug)]
36 struct FooValue(String);
37
38 impl ValueObject for FooValue {}
39
40 impl WrappedInner for FooValue {
41 type Inner = String;
42 }
43
44 impl Wrapped for FooValue {
45 fn as_inner(&self) -> &Self::Inner {
46 &self.0
47 }
48
49 fn into_inner(self) -> Self::Inner {
50 self.0
51 }
52 }
53
54 impl TryFrom<String> for FooValue {
55 type Error = ValidationError;
56
57 fn try_from(value: String) -> Result<Self, Self::Error> {
58 if value.trim().is_empty() {
59 return Err(ValidationError::new("FooValue", "empty"));
60 }
61 Ok(Self(value))
62 }
63 }
64
65 #[test]
66 fn try_from_non_empty_string_succeeds() {
67 assert!(FooValue::try_from("value".to_string()).is_ok())
68 }
69
70 #[test]
71 fn as_inner_returns_reference_to_original_value() {
72 let foo = FooValue("value".to_string());
73 assert_eq!(foo.as_inner(), "value")
74 }
75
76 #[test]
77 fn into_inner_returns_owned_original_value() {
78 let foo = FooValue("value".to_string());
79 assert_eq!(foo.into_inner(), "value".to_string())
80 }
81
82 #[test]
83 fn round_trip_preserves_value() {
84 let foo = FooValue::try_from("value".to_string()).unwrap();
85 assert_eq!(foo.into_inner(), "value".to_string())
86 }
87
88 #[test]
89 fn clone_produces_equal_value() {
90 let foo = FooValue("value".to_string());
91 let bar = foo.clone();
92
93 assert_eq!(foo, bar);
94 assert_eq!(foo.into_inner(), bar.into_inner());
95 }
96
97 #[test]
98 fn eq_returns_true_for_same_inner_value() {
99 let foo = FooValue("foo".to_string());
100 let bar = FooValue("foo".to_string());
101
102 assert_eq!(foo, bar);
103 assert_eq!(foo.into_inner(), bar.into_inner());
104 }
105
106 #[test]
107 fn eq_returns_false_for_different_inner_value() {
108 let foo = FooValue("foo".to_string());
109 let bar = FooValue("bar".to_string());
110
111 assert_ne!(foo, bar);
112 assert_ne!(foo.into_inner(), bar.into_inner());
113 }
114
115 #[test]
116 fn try_from_empty_string_returns_err() {
117 assert!(FooValue::try_from("".to_string()).is_err());
118 assert!(FooValue::try_from(" ".to_string()).is_err());
119 }
120
121 #[test]
122 fn try_from_empty_string_error_has_expected_type_name() {
123 let err = FooValue::try_from("".to_string()).unwrap_err();
124 assert_eq!(err.type_name, "FooValue")
125 }
126
127 #[test]
128 fn try_from_empty_string_error_has_expected_reason() {
129 let err = FooValue::try_from("".to_string()).unwrap_err();
130 assert_eq!(err.reason, "empty")
131 }
132
133 #[test]
134 fn try_from_empty_string_error_display_message() {
135 let err = FooValue::try_from("".to_string()).unwrap_err();
136 assert_eq!(format!("{err}"), "invalid FooValue: empty")
137 }
138}