evidentsource_client/conversions/
constraints.rs1use evidentsource_core::domain::{AppendCondition, EventSelector, Range};
6
7use crate::com::evidentsource as proto;
8
9use super::error::ConversionError;
10
11impl From<AppendCondition> for proto::AppendCondition {
16 fn from(condition: AppendCondition) -> Self {
17 use proto::append_condition::Condition;
18
19 let condition_variant = match condition {
20 AppendCondition::Min(selector, revision) => {
21 Condition::Min(proto::append_condition::Min {
22 selector: Some(selector.into()),
23 revision,
24 })
25 }
26 AppendCondition::Max(selector, revision) => {
27 Condition::Max(proto::append_condition::Max {
28 selector: Some(selector.into()),
29 revision,
30 })
31 }
32 AppendCondition::Range(selector, range) => {
33 Condition::Range(proto::append_condition::Range {
34 selector: Some(selector.into()),
35 min: range.min(),
36 max: range.max(),
37 })
38 }
39 };
40
41 proto::AppendCondition {
42 condition: Some(condition_variant),
43 }
44 }
45}
46
47impl TryFrom<proto::AppendCondition> for AppendCondition {
52 type Error = ConversionError;
53
54 fn try_from(proto: proto::AppendCondition) -> Result<Self, Self::Error> {
55 use proto::append_condition::Condition;
56
57 let condition = proto
58 .condition
59 .ok_or_else(|| ConversionError::missing_oneof("AppendCondition", "condition"))?;
60
61 match condition {
62 Condition::Min(min) => {
63 let selector_proto = min.selector.ok_or_else(|| {
64 ConversionError::missing_field("AppendCondition.Min", "selector")
65 })?;
66 let selector = EventSelector::try_from(selector_proto)
67 .map_err(|e| ConversionError::nested("AppendCondition.Min.selector", e))?;
68 Ok(AppendCondition::Min(selector, min.revision))
69 }
70 Condition::Max(max) => {
71 let selector_proto = max.selector.ok_or_else(|| {
72 ConversionError::missing_field("AppendCondition.Max", "selector")
73 })?;
74 let selector = EventSelector::try_from(selector_proto)
75 .map_err(|e| ConversionError::nested("AppendCondition.Max.selector", e))?;
76 Ok(AppendCondition::Max(selector, max.revision))
77 }
78 Condition::Range(range) => {
79 let selector_proto = range.selector.ok_or_else(|| {
80 ConversionError::missing_field("AppendCondition.Range", "selector")
81 })?;
82 let selector = EventSelector::try_from(selector_proto)
83 .map_err(|e| ConversionError::nested("AppendCondition.Range.selector", e))?;
84
85 let domain_range = Range::new(range.min, range.max).map_err(|_| {
86 ConversionError::InvalidRange {
87 min: range.min,
88 max: range.max,
89 }
90 })?;
91
92 Ok(AppendCondition::Range(selector, domain_range))
93 }
94 }
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 fn make_selector(name: &str) -> EventSelector {
103 EventSelector::event_type_equals(name).unwrap()
104 }
105
106 #[test]
107 fn test_roundtrip_min_condition() {
108 let selector = make_selector("test-type");
109 let domain = AppendCondition::min(selector, 42);
110 let proto: proto::AppendCondition = domain.clone().into();
111 let back: AppendCondition = proto.try_into().unwrap();
112 assert_eq!(domain, back);
113 }
114
115 #[test]
116 fn test_roundtrip_max_condition() {
117 let selector = make_selector("test-type");
118 let domain = AppendCondition::max(selector, 100);
119 let proto: proto::AppendCondition = domain.clone().into();
120 let back: AppendCondition = proto.try_into().unwrap();
121 assert_eq!(domain, back);
122 }
123
124 #[test]
125 fn test_roundtrip_range_condition() {
126 let selector = make_selector("test-type");
127 let range = Range::new(10, 50).unwrap();
128 let domain = AppendCondition::range(selector, range);
129 let proto: proto::AppendCondition = domain.clone().into();
130 let back: AppendCondition = proto.try_into().unwrap();
131 assert_eq!(domain, back);
132 }
133
134 #[test]
135 fn test_invalid_range_returns_error() {
136 let proto = proto::AppendCondition {
137 condition: Some(proto::append_condition::Condition::Range(
138 proto::append_condition::Range {
139 selector: Some(proto::EventSelector {
140 selector: Some(proto::event_selector::Selector::Equals(
141 proto::EventAttribute {
142 attribute: Some(proto::event_attribute::Attribute::Stream(
143 "my-stream".to_string(),
144 )),
145 },
146 )),
147 }),
148 min: 100,
149 max: 50, },
151 )),
152 };
153
154 let result: Result<AppendCondition, _> = proto.try_into();
155 assert!(matches!(result, Err(ConversionError::InvalidRange { .. })));
156 }
157}