ethabi_next/
filter.rs

1// Copyright 2015-2020 Parity Technologies
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use crate::{Hash, Token};
10use serde::{Serialize, Serializer};
11use serde_json::Value;
12use std::ops;
13
14/// Raw topic filter.
15#[derive(Debug, PartialEq, Default)]
16pub struct RawTopicFilter {
17	/// Topic.
18	pub topic0: Topic<Token>,
19	/// Topic.
20	pub topic1: Topic<Token>,
21	/// Topic.
22	pub topic2: Topic<Token>,
23}
24
25/// Topic filter.
26#[derive(Debug, PartialEq, Default)]
27pub struct TopicFilter {
28	/// Usually (for not-anonymous transactions) the first topic is event signature.
29	pub topic0: Topic<Hash>,
30	/// Second topic.
31	pub topic1: Topic<Hash>,
32	/// Third topic.
33	pub topic2: Topic<Hash>,
34	/// Fourth topic.
35	pub topic3: Topic<Hash>,
36}
37
38impl Serialize for TopicFilter {
39	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
40	where
41		S: Serializer,
42	{
43		vec![&self.topic0, &self.topic1, &self.topic2, &self.topic3].serialize(serializer)
44	}
45}
46
47/// Acceptable topic possibilities.
48#[derive(Debug, PartialEq)]
49pub enum Topic<T> {
50	/// Match any.
51	Any,
52	/// Match any of the hashes.
53	OneOf(Vec<T>),
54	/// Match only this hash.
55	This(T),
56}
57
58impl<T> Topic<T> {
59	/// Map
60	pub fn map<F, O>(self, f: F) -> Topic<O>
61	where
62		F: Fn(T) -> O,
63	{
64		match self {
65			Topic::Any => Topic::Any,
66			Topic::OneOf(topics) => Topic::OneOf(topics.into_iter().map(f).collect()),
67			Topic::This(topic) => Topic::This(f(topic)),
68		}
69	}
70
71	/// Returns true if topic is empty (Topic::Any)
72	pub fn is_any(&self) -> bool {
73		match *self {
74			Topic::Any => true,
75			Topic::This(_) | Topic::OneOf(_) => false,
76		}
77	}
78}
79
80impl<T> Default for Topic<T> {
81	fn default() -> Self {
82		Topic::Any
83	}
84}
85
86impl<T> From<Option<T>> for Topic<T> {
87	fn from(o: Option<T>) -> Self {
88		match o {
89			Some(topic) => Topic::This(topic),
90			None => Topic::Any,
91		}
92	}
93}
94
95impl<T> From<T> for Topic<T> {
96	fn from(topic: T) -> Self {
97		Topic::This(topic)
98	}
99}
100
101impl<T> From<Vec<T>> for Topic<T> {
102	fn from(topics: Vec<T>) -> Self {
103		Topic::OneOf(topics)
104	}
105}
106
107impl<T> Into<Vec<T>> for Topic<T> {
108	fn into(self) -> Vec<T> {
109		match self {
110			Topic::Any => vec![],
111			Topic::This(topic) => vec![topic],
112			Topic::OneOf(topics) => topics,
113		}
114	}
115}
116
117impl Serialize for Topic<Hash> {
118	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
119	where
120		S: Serializer,
121	{
122		let value = match *self {
123			Topic::Any => Value::Null,
124			Topic::OneOf(ref vec) => {
125				let v = vec.iter().map(|h| format!("0x{:x}", h)).map(Value::String).collect();
126				Value::Array(v)
127			}
128			Topic::This(ref hash) => Value::String(format!("0x{:x}", hash)),
129		};
130		value.serialize(serializer)
131	}
132}
133
134impl<T> ops::Index<usize> for Topic<T> {
135	type Output = T;
136
137	fn index(&self, index: usize) -> &Self::Output {
138		match *self {
139			Topic::Any => panic!("Topic unavailable"),
140			Topic::This(ref topic) => {
141				if index != 0 {
142					panic!("Topic unavailable");
143				}
144				topic
145			}
146			Topic::OneOf(ref topics) => topics.index(index),
147		}
148	}
149}
150
151#[cfg(test)]
152mod tests {
153	use super::{Topic, TopicFilter};
154	use crate::Hash;
155
156	fn hash(s: &'static str) -> Hash {
157		s.parse().unwrap()
158	}
159
160	#[test]
161	fn test_topic_filter_serialization() {
162		let expected = r#"["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b",null,["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b","0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"],null]"#;
163
164		let topic = TopicFilter {
165			topic0: Topic::This(hash("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")),
166			topic1: Topic::Any,
167			topic2: Topic::OneOf(vec![
168				hash("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
169				hash("0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"),
170			]),
171			topic3: Topic::Any,
172		};
173
174		let topic_str = serde_json::to_string(&topic).unwrap();
175		assert_eq!(expected, &topic_str);
176	}
177
178	#[test]
179	fn test_topic_from() {
180		assert_eq!(Topic::Any as Topic<u64>, None.into());
181		assert_eq!(Topic::This(10u64), 10u64.into());
182		assert_eq!(Topic::OneOf(vec![10u64, 20]), vec![10u64, 20].into());
183	}
184
185	#[test]
186	fn test_topic_into_vec() {
187		let expected: Vec<u64> = vec![];
188		let is: Vec<u64> = (Topic::Any as Topic<u64>).into();
189		assert_eq!(expected, is);
190		let expected: Vec<u64> = vec![10];
191		let is: Vec<u64> = Topic::This(10u64).into();
192		assert_eq!(expected, is);
193		let expected: Vec<u64> = vec![10, 20];
194		let is: Vec<u64> = Topic::OneOf(vec![10u64, 20]).into();
195		assert_eq!(expected, is);
196	}
197
198	#[test]
199	fn test_topic_is_any() {
200		assert!((Topic::Any as Topic<u8>).is_any());
201		assert!(!Topic::OneOf(vec![10u64, 20]).is_any());
202		assert!(!Topic::This(10u64).is_any());
203	}
204
205	#[test]
206	fn test_topic_index() {
207		assert_eq!(Topic::OneOf(vec![10u64, 20])[0], 10);
208		assert_eq!(Topic::OneOf(vec![10u64, 20])[1], 20);
209		assert_eq!(Topic::This(10u64)[0], 10);
210	}
211
212	#[test]
213	#[should_panic(expected = "Topic unavailable")]
214	fn test_topic_index_panic() {
215		let _ = (Topic::Any as Topic<u8>)[0];
216	}
217
218	#[test]
219	#[should_panic(expected = "Topic unavailable")]
220	fn test_topic_index_panic2() {
221		assert_eq!(Topic::This(10u64)[1], 10);
222	}
223}