use evidentsource_core::domain::{
EventAttribute, EventAttributePrefix, EventSelector, EventSubject, EventType, StreamName,
};
use crate::com::evidentsource as proto;
use super::error::ConversionError;
impl From<EventSelector> for proto::EventSelector {
fn from(selector: EventSelector) -> Self {
use proto::event_selector::Selector;
let selector_variant = match selector {
EventSelector::Equals(attr) => Selector::Equals(attr.into()),
EventSelector::StartsWith(prefix) => Selector::StartsWith(prefix.into()),
EventSelector::And { left, right } => {
Selector::And(Box::new(proto::event_selector::LogicalAnd {
left: Some(Box::new((*left).into())),
right: Some(Box::new((*right).into())),
}))
}
EventSelector::Or { left, right } => {
Selector::Or(Box::new(proto::event_selector::LogicalOr {
left: Some(Box::new((*left).into())),
right: Some(Box::new((*right).into())),
}))
}
};
proto::EventSelector {
selector: Some(selector_variant),
}
}
}
impl From<EventAttribute> for proto::EventAttribute {
fn from(attr: EventAttribute) -> Self {
use proto::event_attribute::Attribute;
let attribute = match attr {
EventAttribute::Stream(name) => Attribute::Stream(name.to_string()),
EventAttribute::Subject(subject) => {
Attribute::Subject(proto::event_attribute::SubjectValue {
has_value: subject.is_some(),
value: subject.map(|s| s.to_string()).unwrap_or_default(),
})
}
EventAttribute::EventType(et) => Attribute::EventType(et.to_string()),
};
proto::EventAttribute {
attribute: Some(attribute),
}
}
}
impl From<EventAttributePrefix> for proto::event_selector::StartsWith {
fn from(prefix: EventAttributePrefix) -> Self {
use proto::event_selector::starts_with::Attribute;
let attribute = match prefix {
EventAttributePrefix::Stream(name) => Attribute::Stream(name.to_string()),
EventAttributePrefix::Subject(subject) => Attribute::Subject(subject.to_string()),
EventAttributePrefix::EventType(et) => Attribute::EventType(et.to_string()),
};
proto::event_selector::StartsWith {
attribute: Some(attribute),
}
}
}
impl TryFrom<proto::EventSelector> for EventSelector {
type Error = ConversionError;
fn try_from(proto: proto::EventSelector) -> Result<Self, Self::Error> {
use proto::event_selector::Selector;
let selector = proto
.selector
.ok_or_else(|| ConversionError::missing_oneof("EventSelector", "selector"))?;
match selector {
Selector::Equals(attr) => {
let domain_attr = EventAttribute::try_from(attr)?;
Ok(EventSelector::Equals(domain_attr))
}
Selector::StartsWith(starts_with) => {
let domain_prefix = EventAttributePrefix::try_from(starts_with)?;
Ok(EventSelector::StartsWith(domain_prefix))
}
Selector::And(logical_and) => {
let left = logical_and.left.ok_or_else(|| {
ConversionError::missing_field("EventSelector.LogicalAnd", "left")
})?;
let right = logical_and.right.ok_or_else(|| {
ConversionError::missing_field("EventSelector.LogicalAnd", "right")
})?;
let left_domain = EventSelector::try_from(*left)
.map_err(|e| ConversionError::nested("And.left", e))?;
let right_domain = EventSelector::try_from(*right)
.map_err(|e| ConversionError::nested("And.right", e))?;
Ok(EventSelector::And {
left: Box::new(left_domain),
right: Box::new(right_domain),
})
}
Selector::Or(logical_or) => {
let left = logical_or.left.ok_or_else(|| {
ConversionError::missing_field("EventSelector.LogicalOr", "left")
})?;
let right = logical_or.right.ok_or_else(|| {
ConversionError::missing_field("EventSelector.LogicalOr", "right")
})?;
let left_domain = EventSelector::try_from(*left)
.map_err(|e| ConversionError::nested("Or.left", e))?;
let right_domain = EventSelector::try_from(*right)
.map_err(|e| ConversionError::nested("Or.right", e))?;
Ok(EventSelector::Or {
left: Box::new(left_domain),
right: Box::new(right_domain),
})
}
}
}
}
impl TryFrom<proto::EventAttribute> for EventAttribute {
type Error = ConversionError;
fn try_from(proto: proto::EventAttribute) -> Result<Self, Self::Error> {
use proto::event_attribute::Attribute;
let attr = proto
.attribute
.ok_or_else(|| ConversionError::missing_oneof("EventAttribute", "attribute"))?;
match attr {
Attribute::Stream(s) => {
let name = StreamName::new(s)?;
Ok(EventAttribute::Stream(name))
}
Attribute::Subject(subj) => {
if subj.has_value {
let subject = EventSubject::new(subj.value)?;
Ok(EventAttribute::Subject(Some(subject)))
} else {
Ok(EventAttribute::Subject(None))
}
}
Attribute::EventType(et) => {
let event_type = EventType::new(et)?;
Ok(EventAttribute::EventType(event_type))
}
}
}
}
impl TryFrom<proto::event_selector::StartsWith> for EventAttributePrefix {
type Error = ConversionError;
fn try_from(proto: proto::event_selector::StartsWith) -> Result<Self, Self::Error> {
use proto::event_selector::starts_with::Attribute;
let attr = proto
.attribute
.ok_or_else(|| ConversionError::missing_oneof("StartsWith", "attribute"))?;
match attr {
Attribute::Stream(s) => {
let name = StreamName::new(s)?;
Ok(EventAttributePrefix::Stream(name))
}
Attribute::Subject(s) => {
let subject = EventSubject::new(s)?;
Ok(EventAttributePrefix::Subject(subject))
}
Attribute::EventType(et) => {
let event_type = EventType::new(et)?;
Ok(EventAttributePrefix::EventType(event_type))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_roundtrip_simple_equals() {
let domain = EventSelector::stream_equals("my-stream").unwrap();
let proto: proto::EventSelector = domain.clone().into();
let back: EventSelector = proto.try_into().unwrap();
assert_eq!(domain, back);
}
#[test]
fn test_roundtrip_nested_and_or() {
let domain = EventSelector::stream_equals("stream1")
.unwrap()
.and(EventSelector::event_type_equals("type1").unwrap())
.or(EventSelector::subject_equals("subject1").unwrap());
let proto: proto::EventSelector = domain.clone().into();
let back: EventSelector = proto.try_into().unwrap();
assert_eq!(domain, back);
}
#[test]
fn test_missing_oneof_returns_error() {
let proto = proto::EventSelector { selector: None };
let result: Result<EventSelector, _> = proto.try_into();
assert!(matches!(result, Err(ConversionError::MissingOneof { .. })));
}
#[test]
fn test_no_subject_roundtrip() {
let domain = EventSelector::no_subject();
let proto: proto::EventSelector = domain.clone().into();
let back: EventSelector = proto.try_into().unwrap();
assert_eq!(domain, back);
}
}