1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// This file is part of Frontier.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use ethereum_types::U64;
use serde::{de, Deserialize, Serialize};

/// The syncing status of client.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SyncingStatus {
	/// Progress when syncing.
	IsSyncing(SyncingProgress),
	/// Not syncing.
	NotSyncing,
}

impl serde::Serialize for SyncingStatus {
	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
	where
		S: serde::Serializer,
	{
		match self {
			Self::IsSyncing(progress) => progress.serialize(serializer),
			Self::NotSyncing => serializer.serialize_bool(false),
		}
	}
}

impl<'de> serde::Deserialize<'de> for SyncingStatus {
	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
	where
		D: serde::Deserializer<'de>,
	{
		#[derive(Deserialize)]
		#[serde(untagged)]
		enum Syncing {
			IsSyncing(SyncingProgress),
			NotSyncing(bool),
		}

		match Syncing::deserialize(deserializer)? {
			Syncing::IsSyncing(sync) => Ok(Self::IsSyncing(sync)),
			Syncing::NotSyncing(false) => Ok(Self::NotSyncing),
			Syncing::NotSyncing(true) => Err(de::Error::custom(
				"eth_syncing should always return false if not syncing.",
			)),
		}
	}
}

/// The syncing progress.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncingProgress {
	/// Block number this node started to synchronize from.
	pub starting_block: U64,
	/// Block number this node is currently importing.
	pub current_block: U64,
	/// Block number of the highest block header this node has received from peers.
	pub highest_block: U64,
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn syncing_status_serde_impl() {
		let valid_cases = [
			(
				r#"{"startingBlock":"0x64","currentBlock":"0xc8","highestBlock":"0x12c"}"#,
				SyncingStatus::IsSyncing(SyncingProgress {
					starting_block: 100.into(),
					current_block: 200.into(),
					highest_block: 300.into(),
				}),
			),
			("false", SyncingStatus::NotSyncing),
		];
		for (raw, typed) in valid_cases {
			let deserialized = serde_json::from_str::<SyncingStatus>(raw).unwrap();
			assert_eq!(deserialized, typed);

			let serialized = serde_json::to_string(&typed).unwrap();
			assert_eq!(serialized, raw);
		}

		let invalid_cases = ["true"];
		for raw in invalid_cases {
			let status: Result<SyncingStatus, _> = serde_json::from_str(raw);
			assert!(status.is_err());
		}
	}
}