substreams_database_change/
change.rs1use crate::pb::database::Field;
2use std::str;
3use substreams::pb::substreams::store_delta::Operation;
4use substreams::scalar::{BigDecimal, BigInt};
5use substreams::store::{
6 DeltaBigDecimal, DeltaBigInt, DeltaBool, DeltaBytes, DeltaInt32, DeltaInt64, DeltaString,
7};
8use substreams::Hex;
9
10pub trait ToField {
11 fn to_field<N: AsRef<str>>(self, name: N) -> Field;
12}
13
14pub trait AsString {
17 fn as_string(self) -> String;
18}
19
20macro_rules! impl_as_string_via_to_string {
21 ($name:ty) => {
22 impl AsString for $name {
23 fn as_string(self) -> String {
24 self.to_string()
25 }
26 }
27 };
28}
29
30impl_as_string_via_to_string!(i8);
31impl_as_string_via_to_string!(i16);
32impl_as_string_via_to_string!(i32);
33impl_as_string_via_to_string!(i64);
34impl_as_string_via_to_string!(u8);
35impl_as_string_via_to_string!(u16);
36impl_as_string_via_to_string!(u32);
37impl_as_string_via_to_string!(u64);
38impl_as_string_via_to_string!(String);
39impl_as_string_via_to_string!(&String);
40impl_as_string_via_to_string!(&str);
41impl_as_string_via_to_string!(bool);
42impl_as_string_via_to_string!(BigDecimal);
43impl_as_string_via_to_string!(&BigDecimal);
44impl_as_string_via_to_string!(BigInt);
45impl_as_string_via_to_string!(&BigInt);
46impl_as_string_via_to_string!(::prost_types::Timestamp);
47impl_as_string_via_to_string!(&::prost_types::Timestamp);
48
49impl<T: AsRef<[u8]>> AsString for Hex<T> {
50 fn as_string(self) -> String {
51 self.to_string()
52 }
53}
54
55impl<T: AsRef<[u8]>> AsString for &Hex<T> {
56 fn as_string(self) -> String {
57 self.to_string()
58 }
59}
60
61impl AsString for Vec<u8> {
62 fn as_string(self) -> String {
63 Hex::encode(self)
64 }
65}
66
67impl AsString for &Vec<u8> {
68 fn as_string(self) -> String {
69 Hex::encode(self)
70 }
71}
72
73impl<T: AsString> ToField for (T, T) {
74 fn to_field<N: AsRef<str>>(self, name: N) -> Field {
75 Field {
76 name: name.as_ref().to_string(),
77 old_value: self.0.as_string(),
78 new_value: self.1.as_string(),
79 }
80 }
81}
82
83impl<T: AsString> ToField for (Option<T>, T) {
84 fn to_field<N: AsRef<str>>(self, name: N) -> Field {
85 match self {
86 (Some(old), new) => ToField::to_field((old, new), name),
87 (None, new) => Field {
88 name: name.as_ref().to_string(),
89 old_value: "".to_string(),
90 new_value: new.as_string(),
91 },
92 }
93 }
94}
95
96impl<T: AsString> ToField for (T, Option<T>) {
97 fn to_field<N: AsRef<str>>(self, name: N) -> Field {
98 match self {
99 (old, Some(new)) => ToField::to_field((old, new), name),
100 (old, None) => Field {
101 name: name.as_ref().to_string(),
102 old_value: old.as_string(),
103 new_value: "".to_string(),
104 },
105 }
106 }
107}
108
109fn delta_to_field<T: AsString>(
110 name: &str,
111 operation: Operation,
112 old_value: T,
113 new_value: T,
114) -> Field {
115 match Operation::from(operation) {
116 Operation::Update => ToField::to_field((old_value, new_value), name),
117 Operation::Create => ToField::to_field((None, new_value), name),
118 Operation::Delete => ToField::to_field((None, new_value), name),
119 Operation::Unset => panic!("unsupported operation {:?}", Operation::from(operation)),
120 }
121}
122
123macro_rules! impl_to_field_from_delta_via_move {
124 ($type:ty) => {
125 impl ToField for $type {
126 fn to_field<N: AsRef<str>>(self, name: N) -> Field {
127 delta_to_field(
128 name.as_ref(),
129 self.operation,
130 self.old_value,
131 self.new_value,
132 )
133 }
134 }
135 };
136}
137
138macro_rules! impl_to_field_from_delta_via_ref {
139 ($type:ty) => {
140 impl ToField for $type {
141 fn to_field<N: AsRef<str>>(self, name: N) -> Field {
142 delta_to_field(
143 name.as_ref(),
144 self.operation,
145 &self.old_value,
146 &self.new_value,
147 )
148 }
149 }
150 };
151}
152
153impl_to_field_from_delta_via_move!(&DeltaInt32);
154impl_to_field_from_delta_via_move!(&DeltaInt64);
155impl_to_field_from_delta_via_ref!(&DeltaBigDecimal);
156impl_to_field_from_delta_via_ref!(&DeltaBigInt);
157impl_to_field_from_delta_via_move!(&DeltaBool);
158impl_to_field_from_delta_via_ref!(&DeltaBytes);
159impl_to_field_from_delta_via_ref!(&DeltaString);
160
161#[cfg(test)]
162mod test {
163 use crate::change::ToField;
164 use crate::pb::database::Field;
165 use substreams::pb::substreams::store_delta::Operation;
166 use substreams::scalar::{BigDecimal, BigInt};
167 use substreams::store::{DeltaBigDecimal, DeltaBigInt, DeltaBool, DeltaBytes, DeltaString};
168
169 const FIELD_NAME: &str = "field.name.1";
170
171 #[test]
172 fn i32_change() {
173 let i32_change = (None, 1i32);
174 assert_eq!(
175 create_expected_field(FIELD_NAME, None, Some("1".to_string())),
176 i32_change.to_field(FIELD_NAME)
177 );
178 }
179
180 #[test]
181 fn big_decimal_change() {
182 let bd_change = (None, BigDecimal::from(1 as i32));
183 assert_eq!(
184 create_expected_field(FIELD_NAME, None, Some("1".to_string())),
185 bd_change.to_field(FIELD_NAME)
186 );
187 }
188
189 #[test]
190 fn delta_big_decimal_change() {
191 let delta = DeltaBigDecimal {
192 operation: Operation::Update,
193 ordinal: 0,
194 key: "change".to_string(),
195 old_value: BigDecimal::from(10),
196 new_value: BigDecimal::from(20),
197 };
198
199 assert_eq!(
200 create_expected_field(FIELD_NAME, Some("10".to_string()), Some("20".to_string())),
201 delta.to_field(FIELD_NAME)
202 );
203 }
204
205 #[test]
206 fn big_int_change() {
207 let bi_change = (None, BigInt::from(1 as i32));
208 assert_eq!(
209 create_expected_field(FIELD_NAME, None, Some("1".to_string())),
210 bi_change.to_field(FIELD_NAME)
211 );
212 }
213
214 #[test]
215 fn delta_big_int_change() {
216 let delta = DeltaBigInt {
217 operation: Operation::Update,
218 ordinal: 0,
219 key: "change".to_string(),
220 old_value: BigInt::from(10),
221 new_value: BigInt::from(20),
222 };
223
224 assert_eq!(
225 create_expected_field(FIELD_NAME, Some("10".to_string()), Some("20".to_string())),
226 delta.to_field(FIELD_NAME)
227 );
228 }
229
230 #[test]
231 fn string_change() {
232 let string_change = (None, String::from("string"));
233 assert_eq!(
234 create_expected_field(FIELD_NAME, None, Some("string".to_string())),
235 string_change.to_field(FIELD_NAME)
236 );
237 }
238
239 #[test]
240 fn delta_string_change() {
241 let delta = DeltaString {
242 operation: Operation::Update,
243 ordinal: 0,
244 key: "change".to_string(),
245 old_value: String::from("string1"),
246 new_value: String::from("string2"),
247 };
248
249 assert_eq!(
250 create_expected_field(
251 FIELD_NAME,
252 Some("string1".to_string()),
253 Some("string2".to_string())
254 ),
255 delta.to_field(FIELD_NAME)
256 );
257 }
258
259 #[test]
260 fn bytes_change() {
261 let bytes_change: (Option<Vec<u8>>, Vec<u8>) = (None, Vec::from("bytes"));
262 assert_eq!(
263 create_expected_field(FIELD_NAME, None, Some("6279746573".to_string())),
264 bytes_change.to_field(FIELD_NAME)
265 );
266 }
267
268 #[test]
269 fn delta_bytes_change() {
270 let delta = DeltaBytes {
271 operation: Operation::Update,
272 ordinal: 0,
273 key: "change".to_string(),
274 old_value: Vec::from("bytes1"),
275 new_value: Vec::from("bytes2"),
276 };
277
278 assert_eq!(
279 create_expected_field(
280 FIELD_NAME,
281 Some("627974657331".to_string()),
282 Some("627974657332".to_string())
283 ),
284 delta.to_field(FIELD_NAME)
285 );
286 }
287
288 #[test]
289 fn bool_change() {
290 let bool_change = (None, true);
291 assert_eq!(
292 create_expected_field(FIELD_NAME, None, Some("true".to_string())),
293 bool_change.to_field(FIELD_NAME)
294 );
295 }
296
297 #[test]
298 fn delta_bool_change() {
299 let delta = DeltaBool {
300 operation: Operation::Update,
301 ordinal: 0,
302 key: "change".to_string(),
303 old_value: true,
304 new_value: false,
305 };
306
307 assert_eq!(
308 create_expected_field(FIELD_NAME, Some(true.to_string()), Some(false.to_string()),),
309 delta.to_field(FIELD_NAME)
310 );
311 }
312
313 fn create_expected_field<N: AsRef<str>>(
314 name: N,
315 old_value: Option<String>,
316 new_value: Option<String>,
317 ) -> Field {
318 let mut field = Field {
319 name: name.as_ref().to_string(),
320 ..Default::default()
321 };
322 if old_value.is_some() {
323 field.old_value = old_value.unwrap()
324 }
325 if new_value.is_some() {
326 field.new_value = new_value.unwrap()
327 }
328 field
329 }
330}