1use bincode::{DecodeUntrusted, Encode};
2use platform_serialization::de::Decode;
3use serde::{Deserialize, Serialize};
4
5#[cfg(feature = "json-conversion")]
6use crate::serialization::json_safe_fields;
7#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
8use crate::serialization::JsonConvertible;
9#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
10use crate::serialization::ValueConvertible;
11use crate::{errors::ProtocolError, prelude::TimestampMillis, util::deserializer::ProtocolVersion};
12
13#[cfg_attr(feature = "json-conversion", json_safe_fields)]
14#[derive(
15 Serialize,
16 Deserialize,
17 Encode,
18 Decode,
19 Debug,
20 Default,
21 Clone,
22 Copy,
23 PartialEq,
24 PartialOrd,
25 Eq,
26 DecodeUntrusted,
27)]
28#[serde(rename_all = "camelCase")]
29pub struct Metadata {
30 #[serde(default)]
31 pub block_height: u64,
32 #[serde(default)]
33 pub core_chain_locked_height: u64,
34 #[serde(default)]
35 pub time_ms: TimestampMillis,
36 #[serde(default)]
37 pub protocol_version: ProtocolVersion,
38}
39
40impl std::convert::TryFrom<&str> for Metadata {
41 type Error = ProtocolError;
42
43 fn try_from(d: &str) -> Result<Metadata, Self::Error> {
44 serde_json::from_str(d).map_err(|e| ProtocolError::EncodingError(e.to_string()))
45 }
46}
47
48#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
49impl JsonConvertible for Metadata {}
50
51#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
52impl ValueConvertible for Metadata {}
53
54#[cfg(all(
55 test,
56 feature = "json-conversion",
57 feature = "value-conversion",
58 feature = "serde-conversion"
59))]
60mod json_convertible_tests {
61 use super::*;
62 use crate::serialization::{JsonConvertible, ValueConvertible};
63 use platform_value::platform_value;
64 use serde_json::json;
65
66 fn fixture() -> Metadata {
70 Metadata {
71 block_height: 1_234_567,
72 core_chain_locked_height: 2_222_333,
73 time_ms: 1_700_000_000_000,
74 protocol_version: 9,
75 }
76 }
77
78 #[test]
79 fn json_round_trip_with_full_wire_shape() {
80 let original = fixture();
81 let json = original.to_json().expect("to_json");
82 assert_eq!(
85 json,
86 json!({
87 "blockHeight": 1_234_567,
88 "coreChainLockedHeight": 2_222_333,
89 "timeMs": 1_700_000_000_000u64,
90 "protocolVersion": 9,
91 })
92 );
93 let recovered = Metadata::from_json(json).expect("from_json");
94 assert_eq!(original, recovered);
95 }
96
97 #[test]
98 fn value_round_trip_with_full_wire_shape() {
99 let original = fixture();
100 let value = original.to_object().expect("to_object");
101 assert_eq!(
102 value,
103 platform_value!({
104 "blockHeight": 1_234_567u64,
105 "coreChainLockedHeight": 2_222_333u64,
106 "timeMs": 1_700_000_000_000u64,
107 "protocolVersion": 9u32,
108 })
109 );
110 let recovered = Metadata::from_object(value).expect("from_object");
111 assert_eq!(original, recovered);
112 }
113
114 #[test]
117 fn json_stringifies_unsafe_u64() {
118 let original = Metadata {
119 block_height: u64::MAX,
120 ..fixture()
121 };
122 let json = original.to_json().expect("to_json");
123 assert_eq!(json["blockHeight"], json!(u64::MAX.to_string()));
124 let recovered = Metadata::from_json(json).expect("from_json");
125 assert_eq!(original, recovered);
126 }
127}