web3_etz/types/
sync_state.rs1use crate::types::U256;
2use serde::de::{Deserializer, Error};
3use serde::ser::Serializer;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub struct SyncInfo {
10 pub starting_block: U256,
12
13 pub current_block: U256,
15
16 pub highest_block: U256,
18}
19
20#[derive(Debug, Clone, PartialEq)]
22pub enum SyncState {
23 Syncing(SyncInfo),
25
26 NotSyncing,
28}
29
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32#[serde(rename_all = "PascalCase")]
33struct SubscriptionSyncInfo {
34 pub starting_block: U256,
36
37 pub current_block: U256,
39
40 pub highest_block: U256,
42}
43
44impl From<SubscriptionSyncInfo> for SyncInfo {
45 fn from(s: SubscriptionSyncInfo) -> Self {
46 Self {
47 starting_block: s.starting_block,
48 current_block: s.current_block,
49 highest_block: s.highest_block,
50 }
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
55struct SubscriptionSyncState {
56 pub syncing: bool,
57 pub status: Option<SubscriptionSyncInfo>,
58}
59
60#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
61#[serde(untagged)]
62enum SyncStateVariants {
63 Rpc(SyncInfo),
64 Subscription(SubscriptionSyncState),
65 Boolean(bool),
66}
67
68impl<'de> Deserialize<'de> for SyncState {
72 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
73 where
74 D: Deserializer<'de>,
75 {
76 let v: SyncStateVariants = Deserialize::deserialize(deserializer)?;
77 match v {
78 SyncStateVariants::Rpc(info) => Ok(SyncState::Syncing(info)),
79 SyncStateVariants::Subscription(state) => match state.status {
80 None if !state.syncing => Ok(SyncState::NotSyncing),
81 Some(ref info) if state.syncing => Ok(SyncState::Syncing(info.clone().into())),
82 _ => Err(D::Error::custom(
83 "expected object or `syncing = false`, got `syncing = true`",
84 )),
85 },
86 SyncStateVariants::Boolean(boolean) => {
87 if !boolean {
88 Ok(SyncState::NotSyncing)
89 } else {
90 Err(D::Error::custom("expected object or `false`, got `true`"))
91 }
92 }
93 }
94 }
95}
96
97impl Serialize for SyncState {
98 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
99 where
100 S: Serializer,
101 {
102 match *self {
103 SyncState::Syncing(ref info) => info.serialize(serializer),
104 SyncState::NotSyncing => false.serialize(serializer),
105 }
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::{SyncInfo, SyncState};
112
113 use serde_json;
114
115 #[test]
116 fn should_deserialize_rpc_sync_info() {
117 let sync_state = r#"{
118 "currentBlock": "0x42",
119 "highestBlock": "0x9001",
120 "knownStates": "0x1337",
121 "pulledStates": "0x13",
122 "startingBlock": "0x0"
123 }"#;
124
125 let value: SyncState = serde_json::from_str(sync_state).unwrap();
126
127 assert_eq!(
128 value,
129 SyncState::Syncing(SyncInfo {
130 starting_block: 0x0.into(),
131 current_block: 0x42.into(),
132 highest_block: 0x9001.into()
133 })
134 );
135 }
136
137 #[test]
138 fn should_deserialize_subscription_sync_info() {
139 let sync_state = r#"{
140 "syncing": true,
141 "status": {
142 "CurrentBlock": "0x42",
143 "HighestBlock": "0x9001",
144 "KnownStates": "0x1337",
145 "PulledStates": "0x13",
146 "StartingBlock": "0x0"
147 }
148 }"#;
149
150 let value: SyncState = serde_json::from_str(sync_state).unwrap();
151
152 assert_eq!(
153 value,
154 SyncState::Syncing(SyncInfo {
155 starting_block: 0x0.into(),
156 current_block: 0x42.into(),
157 highest_block: 0x9001.into()
158 })
159 );
160 }
161
162 #[test]
163 fn should_deserialize_boolean_not_syncing() {
164 let sync_state = r#"false"#;
165 let value: SyncState = serde_json::from_str(sync_state).unwrap();
166
167 assert_eq!(value, SyncState::NotSyncing);
168 }
169
170 #[test]
171 fn should_deserialize_subscription_not_syncing() {
172 let sync_state = r#"{
173 "syncing": false
174 }"#;
175
176 let value: SyncState = serde_json::from_str(sync_state).unwrap();
177
178 assert_eq!(value, SyncState::NotSyncing);
179 }
180
181 #[test]
182 fn should_not_deserialize_invalid_boolean_syncing() {
183 let sync_state = r#"true"#;
184 let res: Result<SyncState, _> = serde_json::from_str(sync_state);
185 assert!(res.is_err());
186 }
187
188 #[test]
189 fn should_not_deserialize_invalid_subscription_syncing() {
190 let sync_state = r#"{
191 "syncing": true
192 }"#;
193
194 let res: Result<SyncState, _> = serde_json::from_str(sync_state);
195 assert!(res.is_err());
196 }
197
198 #[test]
199 fn should_not_deserialize_invalid_subscription_not_syncing() {
200 let sync_state = r#"{
201 "syncing": false,
202 "status": {
203 "CurrentBlock": "0x42",
204 "HighestBlock": "0x9001",
205 "KnownStates": "0x1337",
206 "PulledStates": "0x13",
207 "StartingBlock": "0x0"
208 }
209 }"#;
210
211 let res: Result<SyncState, _> = serde_json::from_str(sync_state);
212 assert!(res.is_err());
213 }
214}