rustabi/
filter.rs

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