evidentsource_client/conversions/
selectors.rs1use evidentsource_core::domain::{
4 EventAttribute, EventAttributePrefix, EventSelector, EventSubject, EventType, StreamName,
5};
6
7use crate::com::evidentsource as proto;
8
9use super::error::ConversionError;
10
11impl From<EventSelector> for proto::EventSelector {
16 fn from(selector: EventSelector) -> Self {
17 use proto::event_selector::Selector;
18
19 let selector_variant = match selector {
20 EventSelector::Equals(attr) => Selector::Equals(attr.into()),
21 EventSelector::StartsWith(prefix) => Selector::StartsWith(prefix.into()),
22 EventSelector::And { left, right } => {
23 Selector::And(Box::new(proto::event_selector::LogicalAnd {
24 left: Some(Box::new((*left).into())),
25 right: Some(Box::new((*right).into())),
26 }))
27 }
28 EventSelector::Or { left, right } => {
29 Selector::Or(Box::new(proto::event_selector::LogicalOr {
30 left: Some(Box::new((*left).into())),
31 right: Some(Box::new((*right).into())),
32 }))
33 }
34 };
35
36 proto::EventSelector {
37 selector: Some(selector_variant),
38 }
39 }
40}
41
42impl From<EventAttribute> for proto::EventAttribute {
43 fn from(attr: EventAttribute) -> Self {
44 use proto::event_attribute::Attribute;
45
46 let attribute = match attr {
47 EventAttribute::Stream(name) => Attribute::Stream(name.to_string()),
48 EventAttribute::Subject(subject) => {
49 Attribute::Subject(proto::event_attribute::SubjectValue {
50 has_value: subject.is_some(),
51 value: subject.map(|s| s.to_string()).unwrap_or_default(),
52 })
53 }
54 EventAttribute::EventType(et) => Attribute::EventType(et.to_string()),
55 };
56
57 proto::EventAttribute {
58 attribute: Some(attribute),
59 }
60 }
61}
62
63impl From<EventAttributePrefix> for proto::event_selector::StartsWith {
64 fn from(prefix: EventAttributePrefix) -> Self {
65 use proto::event_selector::starts_with::Attribute;
66
67 let attribute = match prefix {
68 EventAttributePrefix::Stream(name) => Attribute::Stream(name.to_string()),
69 EventAttributePrefix::Subject(subject) => Attribute::Subject(subject.to_string()),
70 EventAttributePrefix::EventType(et) => Attribute::EventType(et.to_string()),
71 };
72
73 proto::event_selector::StartsWith {
74 attribute: Some(attribute),
75 }
76 }
77}
78
79impl TryFrom<proto::EventSelector> for EventSelector {
84 type Error = ConversionError;
85
86 fn try_from(proto: proto::EventSelector) -> Result<Self, Self::Error> {
87 use proto::event_selector::Selector;
88
89 let selector = proto
90 .selector
91 .ok_or_else(|| ConversionError::missing_oneof("EventSelector", "selector"))?;
92
93 match selector {
94 Selector::Equals(attr) => {
95 let domain_attr = EventAttribute::try_from(attr)?;
96 Ok(EventSelector::Equals(domain_attr))
97 }
98 Selector::StartsWith(starts_with) => {
99 let domain_prefix = EventAttributePrefix::try_from(starts_with)?;
100 Ok(EventSelector::StartsWith(domain_prefix))
101 }
102 Selector::And(logical_and) => {
103 let left = logical_and.left.ok_or_else(|| {
104 ConversionError::missing_field("EventSelector.LogicalAnd", "left")
105 })?;
106 let right = logical_and.right.ok_or_else(|| {
107 ConversionError::missing_field("EventSelector.LogicalAnd", "right")
108 })?;
109
110 let left_domain = EventSelector::try_from(*left)
111 .map_err(|e| ConversionError::nested("And.left", e))?;
112 let right_domain = EventSelector::try_from(*right)
113 .map_err(|e| ConversionError::nested("And.right", e))?;
114
115 Ok(EventSelector::And {
116 left: Box::new(left_domain),
117 right: Box::new(right_domain),
118 })
119 }
120 Selector::Or(logical_or) => {
121 let left = logical_or.left.ok_or_else(|| {
122 ConversionError::missing_field("EventSelector.LogicalOr", "left")
123 })?;
124 let right = logical_or.right.ok_or_else(|| {
125 ConversionError::missing_field("EventSelector.LogicalOr", "right")
126 })?;
127
128 let left_domain = EventSelector::try_from(*left)
129 .map_err(|e| ConversionError::nested("Or.left", e))?;
130 let right_domain = EventSelector::try_from(*right)
131 .map_err(|e| ConversionError::nested("Or.right", e))?;
132
133 Ok(EventSelector::Or {
134 left: Box::new(left_domain),
135 right: Box::new(right_domain),
136 })
137 }
138 }
139 }
140}
141
142impl TryFrom<proto::EventAttribute> for EventAttribute {
143 type Error = ConversionError;
144
145 fn try_from(proto: proto::EventAttribute) -> Result<Self, Self::Error> {
146 use proto::event_attribute::Attribute;
147
148 let attr = proto
149 .attribute
150 .ok_or_else(|| ConversionError::missing_oneof("EventAttribute", "attribute"))?;
151
152 match attr {
153 Attribute::Stream(s) => {
154 let name = StreamName::new(s)?;
155 Ok(EventAttribute::Stream(name))
156 }
157 Attribute::Subject(subj) => {
158 if subj.has_value {
159 let subject = EventSubject::new(subj.value)?;
160 Ok(EventAttribute::Subject(Some(subject)))
161 } else {
162 Ok(EventAttribute::Subject(None))
163 }
164 }
165 Attribute::EventType(et) => {
166 let event_type = EventType::new(et)?;
167 Ok(EventAttribute::EventType(event_type))
168 }
169 }
170 }
171}
172
173impl TryFrom<proto::event_selector::StartsWith> for EventAttributePrefix {
174 type Error = ConversionError;
175
176 fn try_from(proto: proto::event_selector::StartsWith) -> Result<Self, Self::Error> {
177 use proto::event_selector::starts_with::Attribute;
178
179 let attr = proto
180 .attribute
181 .ok_or_else(|| ConversionError::missing_oneof("StartsWith", "attribute"))?;
182
183 match attr {
184 Attribute::Stream(s) => {
185 let name = StreamName::new(s)?;
186 Ok(EventAttributePrefix::Stream(name))
187 }
188 Attribute::Subject(s) => {
189 let subject = EventSubject::new(s)?;
190 Ok(EventAttributePrefix::Subject(subject))
191 }
192 Attribute::EventType(et) => {
193 let event_type = EventType::new(et)?;
194 Ok(EventAttributePrefix::EventType(event_type))
195 }
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn test_roundtrip_simple_equals() {
206 let domain = EventSelector::stream_equals("my-stream").unwrap();
207 let proto: proto::EventSelector = domain.clone().into();
208 let back: EventSelector = proto.try_into().unwrap();
209 assert_eq!(domain, back);
210 }
211
212 #[test]
213 fn test_roundtrip_nested_and_or() {
214 let domain = EventSelector::stream_equals("stream1")
215 .unwrap()
216 .and(EventSelector::event_type_equals("type1").unwrap())
217 .or(EventSelector::subject_equals("subject1").unwrap());
218
219 let proto: proto::EventSelector = domain.clone().into();
220 let back: EventSelector = proto.try_into().unwrap();
221 assert_eq!(domain, back);
222 }
223
224 #[test]
225 fn test_missing_oneof_returns_error() {
226 let proto = proto::EventSelector { selector: None };
227 let result: Result<EventSelector, _> = proto.try_into();
228 assert!(matches!(result, Err(ConversionError::MissingOneof { .. })));
229 }
230
231 #[test]
232 fn test_no_subject_roundtrip() {
233 let domain = EventSelector::no_subject();
234 let proto: proto::EventSelector = domain.clone().into();
235 let back: EventSelector = proto.try_into().unwrap();
236 assert_eq!(domain, back);
237 }
238}