evidentsource-client 1.0.0-rc1

Rust client for the EvidentSource event sourcing platform
Documentation
//! StateView type conversions.

use evidentsource_core::domain::{DatabaseName, EventSelector, StateView, StateViewName};

use crate::com::evidentsource as proto;

use super::error::ConversionError;
use super::timestamps::timestamp_to_datetime;

// =============================================================================
// Proto -> Domain
// =============================================================================

impl TryFrom<proto::StateView> for StateView {
    type Error = ConversionError;

    fn try_from(proto: proto::StateView) -> Result<Self, Self::Error> {
        let identity = proto
            .identity
            .ok_or_else(|| ConversionError::missing_field("StateView", "identity"))?;

        let database = DatabaseName::new(&identity.database_name)?;
        let name = StateViewName::new(&identity.state_view_name)?;

        let event_selector_proto = proto
            .event_selector
            .ok_or_else(|| ConversionError::missing_field("StateView", "event_selector"))?;
        let event_selector = EventSelector::try_from(event_selector_proto)?;

        let last_modified_timestamp = proto.last_modified.map(timestamp_to_datetime).transpose()?;

        let last_modified_revision = if proto.last_modified_revision > 0 {
            Some(proto.last_modified_revision)
        } else {
            None
        };

        let content = match proto.content {
            Some(proto::state_view::Content::Data(bytes)) => Some(bytes),
            Some(proto::state_view::Content::Url(_)) => {
                // URL-based content not currently supported, would need separate fetch
                None
            }
            None => None,
        };

        Ok(StateView {
            database,
            name,
            version: identity.state_view_version,
            event_selector,
            last_modified_revision,
            last_modified_timestamp,
            content_type: proto.content_type,
            content_schema_url: proto.content_schema_url.filter(|s| !s.is_empty()),
            content,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_state_view_conversion() {
        let proto = proto::StateView {
            identity: Some(proto::StateViewIdentity {
                database_name: "test-db".to_string(),
                state_view_name: "test-view".to_string(),
                state_view_version: 1,
            }),
            event_selector: Some(proto::EventSelector {
                selector: Some(proto::event_selector::Selector::Equals(
                    proto::EventAttribute {
                        attribute: Some(proto::event_attribute::Attribute::Stream(
                            "my-stream".to_string(),
                        )),
                    },
                )),
            }),
            last_modified: None,
            last_modified_revision: 0,
            content_type: "application/json".to_string(),
            content_schema_url: None,
            content: Some(proto::state_view::Content::Data(
                b"{\"count\": 42}".to_vec(),
            )),
        };

        let domain: StateView = proto.try_into().unwrap();
        assert_eq!(domain.database.as_str(), "test-db");
        assert_eq!(domain.name.as_str(), "test-view");
        assert_eq!(domain.version, 1);
        assert_eq!(domain.content_type, "application/json");
        assert!(domain.content.is_some());
    }
}