stratus/eth/primitives/
block_filter.rs1use std::fmt::Display;
2
3use display_json::DebugAsJson;
4
5use super::PointInTime;
6use crate::alias::JsonValue;
7use crate::eth::primitives::BlockNumber;
8use crate::eth::primitives::Hash;
9
10#[derive(DebugAsJson, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, Hash)]
11#[cfg_attr(test, derive(fake::Dummy))]
12pub enum BlockFilter {
13 #[default]
15 Latest,
16
17 Pending,
19
20 Earliest,
22
23 Hash(Hash),
25
26 Number(BlockNumber),
28}
29
30impl Display for BlockFilter {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 BlockFilter::Latest => write!(f, "latest"),
34 BlockFilter::Pending => write!(f, "pending"),
35 BlockFilter::Earliest => write!(f, "earliest"),
36 BlockFilter::Hash(block_hash) => write!(f, "{block_hash}"),
37 BlockFilter::Number(block_number) => write!(f, "{block_number}"),
38 }
39 }
40}
41
42impl From<PointInTime> for BlockFilter {
43 fn from(point_in_time: PointInTime) -> Self {
44 match point_in_time {
45 PointInTime::Mined => Self::Latest,
46 PointInTime::Pending => Self::Pending,
47 PointInTime::MinedPast(number) => Self::Number(number),
48 }
49 }
50}
51
52impl<'de> serde::Deserialize<'de> for BlockFilter {
57 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
58 where
59 D: serde::Deserializer<'de>,
60 {
61 let value = JsonValue::deserialize(deserializer)?;
62 match value {
63 JsonValue::Null => Ok(Self::Latest),
65
66 serde_json::Value::Number(number) => match number.as_u64() {
68 Some(number) => Ok(Self::Number(BlockNumber::from(number))),
69 None => Err(serde::de::Error::custom("block filter must be zero or a positive integer")),
70 },
71
72 serde_json::Value::String(value) => {
74 match value.as_str() {
75 "latest" | "Latest" => Ok(Self::Latest),
77 "pending" | "Pending" => Ok(Self::Pending),
78 "earliest" | "Earliest" => Ok(Self::Earliest),
79
80 s if s.len() == 64 || s.len() == 66 => {
82 let hash: Hash = s.parse().map_err(serde::de::Error::custom)?;
83 Ok(Self::Hash(hash))
84 }
85 s => {
87 let number: BlockNumber = s.parse().map_err(serde::de::Error::custom)?;
88 Ok(Self::Number(number))
89 }
90 }
91 }
92
93 serde_json::Value::Object(map) => {
94 if map.len() != 1 {
95 return Err(serde::de::Error::custom("value was an object with an unexpected number of fields"));
96 }
97 let Some((key, value)) = map.iter().next() else {
98 return Err(serde::de::Error::custom("value was an object with no fields"));
99 };
100 let Some(value_str) = value.as_str() else {
101 return Err(serde::de::Error::custom("value was an object with non-str fields"));
102 };
103 match key.as_str() {
104 "Hash" => {
105 let hash: Hash = value_str.parse().map_err(serde::de::Error::custom)?;
106 Ok(Self::Hash(hash))
107 }
108 "Number" => {
109 let number: BlockNumber = value_str.parse().map_err(serde::de::Error::custom)?;
110 Ok(Self::Number(number))
111 }
112 _ => Err(serde::de::Error::custom(
113 "value was an object but its field was neither \"Hash\" nor \"Number\"",
114 )),
115 }
116 }
117
118 _ => Err(serde::de::Error::custom("block filter must be a string or integer")),
120 }
121 }
122}
123
124#[cfg(test)]
129mod tests {
130 use serde_json::json;
131
132 use crate::eth::primitives::*;
133
134 #[test]
135 fn serde_block_number_with_latest() {
136 let json = json!("latest");
137 assert_eq!(serde_json::from_value::<BlockFilter>(json).unwrap(), BlockFilter::Latest);
138 }
139
140 #[test]
141 fn serde_block_number_with_number() {
142 let json = json!("0x2");
143 assert_eq!(serde_json::from_value::<BlockFilter>(json).unwrap(), BlockFilter::Number(2usize.into()));
144 }
145}