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