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::BlockTimestampFilter;
9use crate::eth::primitives::Hash;
10
11#[derive(DebugAsJson, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, Hash)]
12#[cfg_attr(test, derive(fake::Dummy))]
13pub enum BlockFilter {
14 #[default]
16 Latest,
17
18 Pending,
20
21 Earliest,
23
24 Hash(Hash),
26
27 Number(BlockNumber),
29
30 Timestamp(BlockTimestampFilter),
32}
33
34impl Display for BlockFilter {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 match self {
37 BlockFilter::Latest => write!(f, "latest"),
38 BlockFilter::Pending => write!(f, "pending"),
39 BlockFilter::Earliest => write!(f, "earliest"),
40 BlockFilter::Hash(block_hash) => write!(f, "{block_hash}"),
41 BlockFilter::Number(block_number) => write!(f, "{block_number}"),
42 BlockFilter::Timestamp(timestamp) => write!(f, "{timestamp:?}"),
43 }
44 }
45}
46
47impl From<PointInTime> for BlockFilter {
48 fn from(point_in_time: PointInTime) -> Self {
49 match point_in_time {
50 PointInTime::Mined => Self::Latest,
51 PointInTime::Pending => Self::Pending,
52 PointInTime::MinedPast(number) => Self::Number(number),
53 }
54 }
55}
56
57impl<'de> serde::Deserialize<'de> for BlockFilter {
62 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
63 where
64 D: serde::Deserializer<'de>,
65 {
66 let value = JsonValue::deserialize(deserializer)?;
67 match value {
68 JsonValue::Null => Ok(Self::Latest),
70
71 serde_json::Value::Number(number) => match number.as_u64() {
73 Some(number) => Ok(Self::Number(BlockNumber::from(number))),
74 None => Err(serde::de::Error::custom("block filter must be zero or a positive integer")),
75 },
76
77 serde_json::Value::String(value) => {
79 match value.as_str() {
80 "latest" | "Latest" => Ok(Self::Latest),
82 "pending" | "Pending" => Ok(Self::Pending),
83 "earliest" | "Earliest" => Ok(Self::Earliest),
84
85 s if s.len() == 64 || s.len() == 66 => {
87 let hash: Hash = s.parse().map_err(serde::de::Error::custom)?;
88 Ok(Self::Hash(hash))
89 }
90 s => {
92 let number: BlockNumber = s.parse().map_err(serde::de::Error::custom)?;
93 Ok(Self::Number(number))
94 }
95 }
96 }
97
98 serde_json::Value::Object(map) => {
99 if map.contains_key("timestamp") {
101 let timestamp_filter: BlockTimestampFilter = serde_json::from_value(serde_json::Value::Object(map))
102 .map_err(|e| serde::de::Error::custom(format!("failed to parse timestamp filter: {e}")))?;
103 return Ok(Self::Timestamp(timestamp_filter));
104 }
105
106 if map.len() != 1 {
108 return Err(serde::de::Error::custom("value was an object with an unexpected number of fields"));
109 }
110 let Some((key, value)) = map.iter().next() else {
111 return Err(serde::de::Error::custom("value was an object with no fields"));
112 };
113
114 match key.as_str() {
115 "Hash" => {
116 let Some(value_str) = value.as_str() else {
117 return Err(serde::de::Error::custom("Hash field must be a string"));
118 };
119 let hash: Hash = value_str.parse().map_err(serde::de::Error::custom)?;
120 Ok(Self::Hash(hash))
121 }
122 "Number" => {
123 let Some(value_str) = value.as_str() else {
124 return Err(serde::de::Error::custom("Number field must be a string"));
125 };
126 let number: BlockNumber = value_str.parse().map_err(serde::de::Error::custom)?;
127 Ok(Self::Number(number))
128 }
129 "Timestamp" => {
130 let timestamp_filter: BlockTimestampFilter =
132 serde_json::from_value(value.clone()).map_err(|e| serde::de::Error::custom(format!("failed to parse timestamp filter: {e}")))?;
133 Ok(Self::Timestamp(timestamp_filter))
134 }
135 _ => Err(serde::de::Error::custom(
136 "value was an object but its field was not one of \"Hash\", \"Number\", \"Timestamp\", or \"timestamp\"",
137 )),
138 }
139 }
140
141 _ => Err(serde::de::Error::custom("block filter must be a string or integer")),
143 }
144 }
145}
146
147#[cfg(test)]
152mod tests {
153 use serde_json::json;
154
155 use crate::eth::primitives::*;
156
157 #[test]
158 fn serde_block_number_with_latest() {
159 let json = json!("latest");
160 assert_eq!(serde_json::from_value::<BlockFilter>(json).unwrap(), BlockFilter::Latest);
161 }
162
163 #[test]
164 fn serde_block_number_with_number() {
165 let json = json!("0x2");
166 assert_eq!(serde_json::from_value::<BlockFilter>(json).unwrap(), BlockFilter::Number(2usize.into()));
167 }
168
169 #[test]
170 fn serde_block_filter_with_timestamp_lowercase() {
171 let json = json!({"timestamp": 1234567890});
172 let result = serde_json::from_value::<BlockFilter>(json).unwrap();
173 match result {
174 BlockFilter::Timestamp(filter) => {
175 assert_eq!(*filter.timestamp, 1234567890);
176 assert_eq!(filter.mode, BlockTimestampSeekMode::ExactOrPrevious);
177 }
178 _ => panic!("Expected BlockFilter::Timestamp"),
179 }
180 }
181
182 #[test]
183 fn serde_block_filter_with_timestamp_and_mode() {
184 let json = json!({"timestamp": 1234567890, "mode": "exactOrNext"});
185 let result = serde_json::from_value::<BlockFilter>(json).unwrap();
186 match result {
187 BlockFilter::Timestamp(filter) => {
188 assert_eq!(*filter.timestamp, 1234567890);
189 assert_eq!(filter.mode, BlockTimestampSeekMode::ExactOrNext);
190 }
191 _ => panic!("Expected BlockFilter::Timestamp"),
192 }
193 }
194
195 #[test]
196 fn serde_block_filter_with_timestamp_capitalized() {
197 let json = json!({"Timestamp": {"timestamp": 1234567890}});
198 let result = serde_json::from_value::<BlockFilter>(json).unwrap();
199 match result {
200 BlockFilter::Timestamp(filter) => {
201 assert_eq!(*filter.timestamp, 1234567890);
202 assert_eq!(filter.mode, BlockTimestampSeekMode::ExactOrPrevious);
203 }
204 _ => panic!("Expected BlockFilter::Timestamp"),
205 }
206 }
207
208 #[test]
209 fn serde_block_filter_with_timestamp_capitalized_and_mode() {
210 let json = json!({"Timestamp": {"timestamp": 1234567890, "mode": "exactOrPrevious"}});
211 let result = serde_json::from_value::<BlockFilter>(json).unwrap();
212 match result {
213 BlockFilter::Timestamp(filter) => {
214 assert_eq!(*filter.timestamp, 1234567890);
215 assert_eq!(filter.mode, BlockTimestampSeekMode::ExactOrPrevious);
216 }
217 _ => panic!("Expected BlockFilter::Timestamp"),
218 }
219 }
220}