//! Message types and conversion methods. use kuska_ssb::api::dto::content::TypedMessage; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::fmt::Debug; use crate::error::GolgiError; /// `SsbMessageContent` is a type alias for `TypedMessage` from the `kuska_ssb` library. /// It is aliased in golgi to fit the naming convention of the other message /// types: `SsbMessageKVT` and `SsbMessageValue`. /// /// See the [kuska source code](https://github.com/Kuska-ssb/ssb/blob/master/src/api/dto/content.rs#L103) for the type definition of `TypedMessage`. pub type SsbMessageContent = TypedMessage; /// The `value` of an SSB message (the `V` in `KVT`). /// /// More information concerning the data model can be found in the /// [`Metadata` documentation](https://spec.scuttlebutt.nz/feed/messages.html#metadata). #[derive(Serialize, Deserialize, Debug)] #[serde(deny_unknown_fields)] #[allow(missing_docs)] pub struct SsbMessageValue { pub previous: Option, pub author: String, pub sequence: u64, pub timestamp: f64, pub hash: String, pub content: Value, pub signature: String, } /// Message content types. #[derive(Debug, Eq, PartialEq)] #[allow(missing_docs)] pub enum SsbMessageContentType { About, Vote, Post, Contact, Unrecognized, } impl SsbMessageValue { /// Get the type field of the message content as an enum, if found. /// /// If no `type` field is found or the `type` field is not a string, /// it returns an `Err(GolgiError::ContentType)`. /// /// If a `type` field is found but with an unknown string, /// it returns an `Ok(SsbMessageContentType::Unrecognized)`. pub fn get_message_type(&self) -> Result { let msg_type = self .content .get("type") .ok_or_else(|| GolgiError::ContentType("type field not found".to_string()))?; let mtype_str: &str = msg_type.as_str().ok_or_else(|| { GolgiError::ContentType("type field value is not a string as expected".to_string()) })?; let enum_type = match mtype_str { "about" => SsbMessageContentType::About, "post" => SsbMessageContentType::Post, "vote" => SsbMessageContentType::Vote, "contact" => SsbMessageContentType::Contact, _ => SsbMessageContentType::Unrecognized, }; Ok(enum_type) } /// Helper function which returns `true` if this message is of the given type, /// and `false` if the type does not match or is not found. pub fn is_message_type(&self, message_type: SsbMessageContentType) -> bool { let self_message_type = self.get_message_type(); match self_message_type { Ok(mtype) => mtype == message_type, Err(_err) => false, } } /// Convert the content JSON value into an `SsbMessageContent` `enum`, /// using the `type` field as a tag to select which variant of the `enum` /// to deserialize into. /// /// See the [Serde docs on internally-tagged enum representations](https://serde.rs/enum-representations.html#internally-tagged) for further details. pub fn into_ssb_message_content(self) -> Result { let m: SsbMessageContent = serde_json::from_value(self.content)?; Ok(m) } } /// An SSB message represented as a key-value-timestamp (`KVT`). /// /// More information concerning the data model can be found in the /// [`Metadata` documentation](https://spec.scuttlebutt.nz/feed/messages.html#metadata). #[derive(Serialize, Deserialize, Debug)] #[serde(deny_unknown_fields)] #[allow(missing_docs)] pub struct SsbMessageKVT { pub key: String, pub value: SsbMessageValue, pub timestamp: Option, pub rts: Option, }