casper_types/contract_messages/
topics.rs1use crate::{
2 bytesrepr::{self, FromBytes, ToBytes},
3 checksummed_hex, BlockTime,
4};
5
6use core::convert::TryFrom;
7
8use alloc::{string::String, vec::Vec};
9use core::fmt::{Debug, Display, Formatter};
10
11#[cfg(feature = "datasize")]
12use datasize::DataSize;
13#[cfg(any(feature = "testing", test))]
14use rand::{
15 distributions::{Distribution, Standard},
16 Rng,
17};
18#[cfg(feature = "json-schema")]
19use schemars::JsonSchema;
20use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
21
22use super::error::FromStrError;
23
24pub const TOPIC_NAME_HASH_LENGTH: usize = 32;
26
27#[derive(Default, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
29#[cfg_attr(feature = "datasize", derive(DataSize))]
30#[cfg_attr(
31 feature = "json-schema",
32 derive(JsonSchema),
33 schemars(description = "The hash of the name of the message topic.")
34)]
35pub struct TopicNameHash(
36 #[cfg_attr(feature = "json-schema", schemars(skip, with = "String"))]
37 pub [u8; TOPIC_NAME_HASH_LENGTH],
38);
39
40impl TopicNameHash {
41 pub const fn new(topic_name_hash: [u8; TOPIC_NAME_HASH_LENGTH]) -> TopicNameHash {
43 TopicNameHash(topic_name_hash)
44 }
45
46 pub fn value(&self) -> [u8; TOPIC_NAME_HASH_LENGTH] {
48 self.0
49 }
50
51 pub fn to_formatted_string(self) -> String {
53 base16::encode_lower(&self.0)
54 }
55
56 pub fn from_formatted_str(input: &str) -> Result<Self, FromStrError> {
58 let bytes =
59 <[u8; TOPIC_NAME_HASH_LENGTH]>::try_from(checksummed_hex::decode(input)?.as_ref())?;
60 Ok(TopicNameHash(bytes))
61 }
62}
63
64impl ToBytes for TopicNameHash {
65 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
66 let mut buffer = bytesrepr::allocate_buffer(self)?;
67 buffer.append(&mut self.0.to_bytes()?);
68 Ok(buffer)
69 }
70
71 fn serialized_length(&self) -> usize {
72 self.0.serialized_length()
73 }
74}
75
76impl FromBytes for TopicNameHash {
77 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
78 let (hash, rem) = FromBytes::from_bytes(bytes)?;
79 Ok((TopicNameHash(hash), rem))
80 }
81}
82
83impl Serialize for TopicNameHash {
84 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
85 if serializer.is_human_readable() {
86 self.to_formatted_string().serialize(serializer)
87 } else {
88 self.0.serialize(serializer)
89 }
90 }
91}
92
93impl<'de> Deserialize<'de> for TopicNameHash {
94 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
95 if deserializer.is_human_readable() {
96 let formatted_string = String::deserialize(deserializer)?;
97 TopicNameHash::from_formatted_str(&formatted_string).map_err(SerdeError::custom)
98 } else {
99 let bytes = <[u8; TOPIC_NAME_HASH_LENGTH]>::deserialize(deserializer)?;
100 Ok(TopicNameHash(bytes))
101 }
102 }
103}
104
105impl Display for TopicNameHash {
106 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
107 write!(f, "{}", base16::encode_lower(&self.0))
108 }
109}
110
111impl Debug for TopicNameHash {
112 fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
113 write!(f, "MessageTopicHash({})", base16::encode_lower(&self.0))
114 }
115}
116
117#[cfg(any(feature = "testing", test))]
118impl Distribution<TopicNameHash> for Standard {
119 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> TopicNameHash {
120 TopicNameHash(rng.gen())
121 }
122}
123
124impl From<[u8; TOPIC_NAME_HASH_LENGTH]> for TopicNameHash {
125 fn from(value: [u8; TOPIC_NAME_HASH_LENGTH]) -> Self {
126 TopicNameHash(value)
127 }
128}
129
130#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
132#[cfg_attr(feature = "datasize", derive(DataSize))]
133#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
134pub struct MessageTopicSummary {
135 pub(crate) message_count: u32,
137 pub(crate) blocktime: BlockTime,
139 pub(crate) topic_name: String,
141}
142
143impl MessageTopicSummary {
144 pub fn new(message_count: u32, blocktime: BlockTime, topic_name: String) -> Self {
146 Self {
147 message_count,
148 blocktime,
149 topic_name,
150 }
151 }
152
153 pub fn message_count(&self) -> u32 {
155 self.message_count
156 }
157
158 pub fn blocktime(&self) -> BlockTime {
160 self.blocktime
161 }
162
163 pub fn topic_name(&self) -> &str {
165 &self.topic_name
166 }
167}
168
169impl ToBytes for MessageTopicSummary {
170 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
171 let mut buffer = bytesrepr::allocate_buffer(self)?;
172 buffer.append(&mut self.message_count.to_bytes()?);
173 buffer.append(&mut self.blocktime.to_bytes()?);
174 buffer.append(&mut self.topic_name.to_bytes()?);
175 Ok(buffer)
176 }
177
178 fn serialized_length(&self) -> usize {
179 self.message_count.serialized_length()
180 + self.blocktime.serialized_length()
181 + self.topic_name.serialized_length()
182 }
183}
184
185impl FromBytes for MessageTopicSummary {
186 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
187 let (message_count, rem) = FromBytes::from_bytes(bytes)?;
188 let (blocktime, rem) = FromBytes::from_bytes(rem)?;
189 let (topic_name, rem) = FromBytes::from_bytes(rem)?;
190 Ok((
191 MessageTopicSummary {
192 message_count,
193 blocktime,
194 topic_name,
195 },
196 rem,
197 ))
198 }
199}
200
201const TOPIC_OPERATION_ADD_TAG: u8 = 0;
202const OPERATION_MAX_SERIALIZED_LEN: usize = 1;
203
204#[derive(Debug, PartialEq)]
206pub enum MessageTopicOperation {
207 Add,
209}
210
211impl MessageTopicOperation {
212 pub const fn max_serialized_len() -> usize {
214 OPERATION_MAX_SERIALIZED_LEN
215 }
216}
217
218impl ToBytes for MessageTopicOperation {
219 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
220 let mut buffer = bytesrepr::allocate_buffer(self)?;
221 match self {
222 MessageTopicOperation::Add => buffer.push(TOPIC_OPERATION_ADD_TAG),
223 }
224 Ok(buffer)
225 }
226
227 fn serialized_length(&self) -> usize {
228 match self {
229 MessageTopicOperation::Add => 1,
230 }
231 }
232}
233
234impl FromBytes for MessageTopicOperation {
235 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
236 let (tag, remainder): (u8, &[u8]) = FromBytes::from_bytes(bytes)?;
237 match tag {
238 TOPIC_OPERATION_ADD_TAG => Ok((MessageTopicOperation::Add, remainder)),
239 _ => Err(bytesrepr::Error::Formatting),
240 }
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use crate::bytesrepr;
247
248 use super::*;
249
250 #[test]
251 fn serialization_roundtrip() {
252 let topic_name_hash = TopicNameHash::new([0x4du8; TOPIC_NAME_HASH_LENGTH]);
253 bytesrepr::test_serialization_roundtrip(&topic_name_hash);
254
255 let topic_summary =
256 MessageTopicSummary::new(10, BlockTime::new(100), "topic_name".to_string());
257 bytesrepr::test_serialization_roundtrip(&topic_summary);
258
259 let topic_operation = MessageTopicOperation::Add;
260 bytesrepr::test_serialization_roundtrip(&topic_operation);
261 }
262
263 #[test]
264 fn json_roundtrip() {
265 let topic_name_hash = TopicNameHash::new([0x4du8; TOPIC_NAME_HASH_LENGTH]);
266 let json_string = serde_json::to_string_pretty(&topic_name_hash).unwrap();
267 let decoded: TopicNameHash = serde_json::from_str(&json_string).unwrap();
268 assert_eq!(decoded, topic_name_hash);
269
270 let topic_summary =
271 MessageTopicSummary::new(10, BlockTime::new(100), "topic_name".to_string());
272 let json_string = serde_json::to_string_pretty(&topic_summary).unwrap();
273 let decoded: MessageTopicSummary = serde_json::from_str(&json_string).unwrap();
274 assert_eq!(decoded, topic_summary);
275 }
276}