dwn-core 0.4.0

Core DWN types.
Documentation
use std::fmt::Display;

use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;

mod protocols;
mod records;

pub use protocols::*;
pub use records::*;
use time::OffsetDateTime;

use super::cid::{CidGenerationError, compute_cid_cbor};

#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
#[serde(untagged)]
pub enum Descriptor {
    ProtocolsConfigure(Box<ProtocolsConfigure>),
    ProtocolsQuery(Box<ProtocolsQuery>),
    RecordsDelete(Box<RecordsDelete>),
    RecordsQuery(Box<RecordsQuery>),
    RecordsRead(Box<RecordsRead>),
    RecordsSync(Box<RecordsSync>),
    RecordsWrite(Box<RecordsWrite>),
}

impl Descriptor {
    pub fn compute_entry_id(&self) -> Result<String, CidGenerationError> {
        #[derive(Serialize)]
        #[serde(rename_all = "camelCase")]
        struct RecordIdGeneration {
            descriptor_cid: String,
        }

        let generator = RecordIdGeneration {
            descriptor_cid: compute_cid_cbor(self)?,
        };

        compute_cid_cbor(&generator)
    }

    pub fn message_timestamp(&self) -> Option<&OffsetDateTime> {
        match self {
            Descriptor::ProtocolsConfigure(_) => None,
            Descriptor::ProtocolsQuery(_) => None,
            Descriptor::RecordsDelete(d) => Some(&d.message_timestamp),
            Descriptor::RecordsQuery(d) => Some(&d.message_timestamp),
            Descriptor::RecordsRead(d) => Some(&d.message_timestamp),
            Descriptor::RecordsSync(d) => Some(&d.message_timestamp),
            Descriptor::RecordsWrite(d) => Some(&d.message_timestamp),
        }
    }
}

impl<'de> Deserialize<'de> for Descriptor {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let raw = Value::deserialize(deserializer)?;

        let Some(interface) = raw.get("interface") else {
            return Err(serde::de::Error::custom("no interface"));
        };
        let interface = serde_json::from_value::<Interface>(interface.clone())
            .map_err(|_| serde::de::Error::custom("unsupported interface"))?;

        let Some(method) = raw.get("method") else {
            return Err(serde::de::Error::custom("no method"));
        };
        let method = serde_json::from_value::<Method>(method.clone())
            .map_err(|_| serde::de::Error::custom("unsupported method"))?;

        match (interface, method) {
            (Interface::Protocols, Method::Configure) => {
                let desc: ProtocolsConfigure =
                    serde_json::from_value(raw).map_err(serde::de::Error::custom)?;
                Ok(Descriptor::ProtocolsConfigure(Box::new(desc)))
            }
            (Interface::Protocols, Method::Query) => {
                let desc: ProtocolsQuery =
                    serde_json::from_value(raw).map_err(serde::de::Error::custom)?;
                Ok(Descriptor::ProtocolsQuery(Box::new(desc)))
            }
            (Interface::Records, Method::Query) => {
                let desc: RecordsQuery =
                    serde_json::from_value(raw).map_err(serde::de::Error::custom)?;
                Ok(Descriptor::RecordsQuery(Box::new(desc)))
            }
            (Interface::Records, Method::Read) => {
                let desc: RecordsRead =
                    serde_json::from_value(raw).map_err(serde::de::Error::custom)?;
                Ok(Descriptor::RecordsRead(Box::new(desc)))
            }
            (Interface::Records, Method::Sync) => {
                let desc: RecordsSync =
                    serde_json::from_value(raw).map_err(serde::de::Error::custom)?;
                Ok(Descriptor::RecordsSync(Box::new(desc)))
            }
            (Interface::Records, Method::Write) => {
                let desc: RecordsWrite =
                    serde_json::from_value(raw).map_err(serde::de::Error::custom)?;
                Ok(Descriptor::RecordsWrite(Box::new(desc)))
            }
            _ => Err(serde::de::Error::custom(
                "Unsupported interface / method combination",
            )),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum Interface {
    Protocols,
    Records,
}

impl Display for Interface {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum Method {
    Configure,
    Delete,
    Query,
    Read,
    Sync,
    Write,
}

impl Display for Method {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

#[cfg(test)]
mod test {
    use mime::TEXT_PLAIN;
    use semver::Version;

    use crate::message::Message;

    use super::*;

    #[test]
    fn test_serialize_records_query() {
        let msg = RecordsQueryBuilder {
            filter: RecordFilter {
                schema: Some("schema".to_string()),
                protocol: Some("protocol".to_string()),
                protocol_version: Some(Version::new(1, 2, 3)),
                record_id: Some("record id".to_string()),
                ..Default::default()
            },
        }
        .build()
        .unwrap();
        let ser = serde_json::to_string_pretty(&msg).unwrap();
        println!("{}", ser);
        let des = serde_json::from_str::<Message>(&ser).unwrap();
        assert_eq!(des, msg);
    }

    #[test]
    fn test_serialize_records_read() {
        let msg = RecordsReadBuilder::new("test".to_string()).build().unwrap();
        let ser = serde_json::to_string_pretty(&msg).unwrap();
        println!("{}", ser);
        let des = serde_json::from_str::<Message>(&ser).unwrap();
        assert_eq!(des, msg);
    }

    #[test]
    fn test_serialize_records_write() {
        let msg = RecordsWriteBuilder {
            context_id: None,
            data: Some(vec![0, 1, 2, 3]),
            data_format: Some(TEXT_PLAIN),
            protocol: Some("protocol".to_string()),
            protocol_version: Some(Version::new(1, 2, 3)),
            protocol_path: Some("protocol path".to_string()),
            published: Some(true),
            record_id: Some("record id".to_string()),
            schema: Some("schema".to_string()),
        }
        .build()
        .unwrap();
        let ser = serde_json::to_string_pretty(&msg).unwrap();
        println!("{}", ser);
        let des = serde_json::from_str::<Message>(&ser).unwrap();
        assert_eq!(des, msg);
    }
}