Skip to main content

aleph_types/message/
pending.rs

1use crate::chain::{Address, Chain, Signature};
2use crate::channel::Channel;
3use crate::item_hash::ItemHash;
4use crate::message::item_type::ItemType;
5use crate::message::{ContentSource, Message, MessageType};
6use crate::timestamp::Timestamp;
7use serde::ser::SerializeStruct;
8use serde::{Serialize, Serializer};
9
10/// A signed message ready for submission to the Aleph network.
11///
12/// The `item_content` field is always present in memory (needed for uploading
13/// storage/IPFS content), but the custom `Serialize` implementation only emits
14/// it when `item_type == Inline`.
15#[derive(Debug, Clone)]
16pub struct PendingMessage {
17    pub chain: Chain,
18    pub sender: Address,
19    pub signature: Signature,
20    pub message_type: MessageType,
21    pub item_type: ItemType,
22    pub item_content: String,
23    pub item_hash: ItemHash,
24    pub time: Timestamp,
25    pub channel: Option<Channel>,
26}
27
28impl Serialize for PendingMessage {
29    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
30        let has_content = self.item_type == ItemType::Inline;
31        let has_channel = self.channel.is_some();
32        let field_count = 7 + has_content as usize + has_channel as usize;
33
34        let mut state = serializer.serialize_struct("PendingMessage", field_count)?;
35        state.serialize_field("sender", &self.sender)?;
36        state.serialize_field("chain", &self.chain)?;
37        state.serialize_field("signature", &self.signature)?;
38        state.serialize_field("type", &self.message_type)?;
39        state.serialize_field("item_type", &self.item_type)?;
40        if has_content {
41            state.serialize_field("item_content", &self.item_content)?;
42        }
43        state.serialize_field("item_hash", &self.item_hash)?;
44        state.serialize_field("time", &self.time)?;
45        if let Some(channel) = &self.channel {
46            state.serialize_field("channel", channel)?;
47        }
48        state.end()
49    }
50}
51
52impl From<&Message> for PendingMessage {
53    fn from(message: &Message) -> Self {
54        let (item_type, item_content) = match &message.content_source {
55            ContentSource::Inline { item_content } => (ItemType::Inline, item_content.clone()),
56            ContentSource::Storage => (ItemType::Storage, String::new()),
57            ContentSource::Ipfs => (ItemType::Ipfs, String::new()),
58        };
59        PendingMessage {
60            chain: message.chain.clone(),
61            sender: message.sender.clone(),
62            signature: message.signature.clone(),
63            message_type: message.message_type,
64            item_type,
65            item_content,
66            item_hash: message.item_hash.clone(),
67            time: message.time.clone(),
68            channel: message.channel.clone(),
69        }
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use crate::{address, item_hash};
77
78    fn make_pending(item_type: ItemType) -> PendingMessage {
79        PendingMessage {
80            chain: Chain::Ethereum,
81            sender: address!("0xABCD"),
82            signature: Signature::from("0xSIG".to_string()),
83            message_type: MessageType::Post,
84            item_type,
85            item_content: r#"{"type":"test","address":"0xABCD","time":1234.0}"#.to_string(),
86            item_hash: item_hash!(
87                "d281eb8a69ba1f4dda2d71aaf3ded06caa92edd690ef3d0632f41aa91167762c"
88            ),
89            time: Timestamp::from(1234.0),
90            channel: None,
91        }
92    }
93
94    #[test]
95    fn test_pending_message_inline_includes_item_content() {
96        let msg = make_pending(ItemType::Inline);
97        let json = serde_json::to_value(&msg).unwrap();
98        assert_eq!(json["item_type"], "inline");
99        assert!(json.get("item_content").is_some());
100        assert_eq!(json["item_content"], msg.item_content);
101    }
102
103    #[test]
104    fn test_pending_message_storage_omits_item_content() {
105        let msg = make_pending(ItemType::Storage);
106        let json = serde_json::to_value(&msg).unwrap();
107        assert_eq!(json["item_type"], "storage");
108        assert!(json.get("item_content").is_none());
109    }
110
111    #[test]
112    fn test_pending_message_ipfs_omits_item_content() {
113        let msg = make_pending(ItemType::Ipfs);
114        let json = serde_json::to_value(&msg).unwrap();
115        assert_eq!(json["item_type"], "ipfs");
116        assert!(json.get("item_content").is_none());
117    }
118}