use evidentsource_core::domain::{AppendCondition, EventSelector, Range};
use crate::com::evidentsource as proto;
use super::error::ConversionError;
impl From<AppendCondition> for proto::AppendCondition {
fn from(condition: AppendCondition) -> Self {
use proto::append_condition::Condition;
let condition_variant = match condition {
AppendCondition::Min(selector, revision) => {
Condition::Min(proto::append_condition::Min {
selector: Some(selector.into()),
revision,
})
}
AppendCondition::Max(selector, revision) => {
Condition::Max(proto::append_condition::Max {
selector: Some(selector.into()),
revision,
})
}
AppendCondition::Range(selector, range) => {
Condition::Range(proto::append_condition::Range {
selector: Some(selector.into()),
min: range.min(),
max: range.max(),
})
}
};
proto::AppendCondition {
condition: Some(condition_variant),
}
}
}
impl TryFrom<proto::AppendCondition> for AppendCondition {
type Error = ConversionError;
fn try_from(proto: proto::AppendCondition) -> Result<Self, Self::Error> {
use proto::append_condition::Condition;
let condition = proto
.condition
.ok_or_else(|| ConversionError::missing_oneof("AppendCondition", "condition"))?;
match condition {
Condition::Min(min) => {
let selector_proto = min.selector.ok_or_else(|| {
ConversionError::missing_field("AppendCondition.Min", "selector")
})?;
let selector = EventSelector::try_from(selector_proto)
.map_err(|e| ConversionError::nested("AppendCondition.Min.selector", e))?;
Ok(AppendCondition::Min(selector, min.revision))
}
Condition::Max(max) => {
let selector_proto = max.selector.ok_or_else(|| {
ConversionError::missing_field("AppendCondition.Max", "selector")
})?;
let selector = EventSelector::try_from(selector_proto)
.map_err(|e| ConversionError::nested("AppendCondition.Max.selector", e))?;
Ok(AppendCondition::Max(selector, max.revision))
}
Condition::Range(range) => {
let selector_proto = range.selector.ok_or_else(|| {
ConversionError::missing_field("AppendCondition.Range", "selector")
})?;
let selector = EventSelector::try_from(selector_proto)
.map_err(|e| ConversionError::nested("AppendCondition.Range.selector", e))?;
let domain_range = Range::new(range.min, range.max).map_err(|_| {
ConversionError::InvalidRange {
min: range.min,
max: range.max,
}
})?;
Ok(AppendCondition::Range(selector, domain_range))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_selector(name: &str) -> EventSelector {
EventSelector::event_type_equals(name).unwrap()
}
#[test]
fn test_roundtrip_min_condition() {
let selector = make_selector("test-type");
let domain = AppendCondition::min(selector, 42);
let proto: proto::AppendCondition = domain.clone().into();
let back: AppendCondition = proto.try_into().unwrap();
assert_eq!(domain, back);
}
#[test]
fn test_roundtrip_max_condition() {
let selector = make_selector("test-type");
let domain = AppendCondition::max(selector, 100);
let proto: proto::AppendCondition = domain.clone().into();
let back: AppendCondition = proto.try_into().unwrap();
assert_eq!(domain, back);
}
#[test]
fn test_roundtrip_range_condition() {
let selector = make_selector("test-type");
let range = Range::new(10, 50).unwrap();
let domain = AppendCondition::range(selector, range);
let proto: proto::AppendCondition = domain.clone().into();
let back: AppendCondition = proto.try_into().unwrap();
assert_eq!(domain, back);
}
#[test]
fn test_invalid_range_returns_error() {
let proto = proto::AppendCondition {
condition: Some(proto::append_condition::Condition::Range(
proto::append_condition::Range {
selector: Some(proto::EventSelector {
selector: Some(proto::event_selector::Selector::Equals(
proto::EventAttribute {
attribute: Some(proto::event_attribute::Attribute::Stream(
"my-stream".to_string(),
)),
},
)),
}),
min: 100,
max: 50, },
)),
};
let result: Result<AppendCondition, _> = proto.try_into();
assert!(matches!(result, Err(ConversionError::InvalidRange { .. })));
}
}