1use std::collections::HashMap;
7
8use once_cell::sync::Lazy;
9
10pub type FieldMapping = HashMap<&'static str, &'static str>;
12
13pub type MethodFieldMappings = HashMap<&'static str, FieldMapping>;
15
16pub static CEL_FIELD_MAPPINGS: Lazy<MethodFieldMappings> = Lazy::new(|| {
18 let mut mappings = HashMap::new();
19
20 mappings.insert(
22 "subscribe_wallet_balance",
23 HashMap::from([
24 ("walletAddress", "a"),
25 ("tokenAddress", "ta"),
26 ("tokenPriceInUsd", "tpiu"),
27 ("balance", "b"),
28 ("timestamp", "t"),
29 ]),
30 );
31
32 mappings.insert(
34 "subscribe_token_candles",
35 HashMap::from([
36 ("open", "o"),
37 ("close", "c"),
38 ("high", "h"),
39 ("low", "l"),
40 ("volume", "v"),
41 ("resolution", "r"),
42 ("time", "t"),
43 ("number", "n"),
44 ]),
45 );
46
47 mappings.insert(
49 "subscribe_token_stats",
50 HashMap::from([
51 ("address", "a"),
52 ("timestamp", "t"),
53 ("buys1m", "b1m"),
54 ("sells1m", "s1m"),
55 ("buyers1m", "be1m"),
56 ("sellers1m", "se1m"),
57 ("buyVolumeInUsd1m", "bviu1m"),
58 ("sellVolumeInUsd1m", "sviu1m"),
59 ("price1m", "p1m"),
60 ("openInUsd1m", "oiu1m"),
61 ("closeInUsd1m", "ciu1m"),
62 ("buys5m", "b5m"),
63 ("sells5m", "s5m"),
64 ("buyers5m", "be5m"),
65 ("sellers5m", "se5m"),
66 ("buyVolumeInUsd5m", "bviu5m"),
67 ("sellVolumeInUsd5m", "sviu5m"),
68 ("price5m", "p5m"),
69 ("openInUsd5m", "oiu5m"),
70 ("closeInUsd5m", "ciu5m"),
71 ("buys15m", "b15m"),
72 ("sells15m", "s15m"),
73 ("buyers15m", "be15m"),
74 ("sellers15m", "se15m"),
75 ("buyVolumeInUsd15m", "bviu15m"),
76 ("sellVolumeInUsd15m", "sviu15m"),
77 ("price15m", "p15m"),
78 ("openInUsd15m", "oiu15m"),
79 ("closeInUsd15m", "ciu15m"),
80 ("buys30m", "b30m"),
81 ("sells30m", "s30m"),
82 ("buyers30m", "be30m"),
83 ("sellers30m", "se30m"),
84 ("buyVolumeInUsd30m", "bviu30m"),
85 ("sellVolumeInUsd30m", "sviu30m"),
86 ("price30m", "p30m"),
87 ("openInUsd30m", "oiu30m"),
88 ("closeInUsd30m", "ciu30m"),
89 ("buys1h", "b1h"),
90 ("sells1h", "s1h"),
91 ("buyers1h", "be1h"),
92 ("sellers1h", "se1h"),
93 ("buyVolumeInUsd1h", "bviu1h"),
94 ("sellVolumeInUsd1h", "sviu1h"),
95 ("price1h", "p1h"),
96 ("openInUsd1h", "oiu1h"),
97 ("closeInUsd1h", "ciu1h"),
98 ("buys4h", "b4h"),
99 ("sells4h", "s4h"),
100 ("buyers4h", "be4h"),
101 ("sellers4h", "se4h"),
102 ("buyVolumeInUsd4h", "bviu4h"),
103 ("sellVolumeInUsd4h", "sviu4h"),
104 ("price4h", "p4h"),
105 ("openInUsd4h", "oiu4h"),
106 ("closeInUsd4h", "ciu4h"),
107 ("buys24h", "b24h"),
108 ("sells24h", "s24h"),
109 ("buyers24h", "be24h"),
110 ("sellers24h", "se24h"),
111 ("buyVolumeInUsd24h", "bviu24h"),
112 ("sellVolumeInUsd24h", "sviu24h"),
113 ("price24h", "p24h"),
114 ("price", "p"),
115 ("openInUsd24h", "oiu24h"),
116 ("closeInUsd24h", "ciu24h"),
117 ]),
118 );
119
120 mappings.insert(
122 "subscribe_token_holders",
123 HashMap::from([
124 ("tokenAddress", "a"),
125 ("holders", "h"),
126 ("top100Amount", "t100a"),
127 ("top10Amount", "t10a"),
128 ("top100Holders", "t100h"),
129 ("top10Holders", "t10h"),
130 ("top100Ratio", "t100r"),
131 ("top10Ratio", "t10r"),
132 ("timestamp", "ts"),
133 ]),
134 );
135
136 mappings.insert(
138 "subscribe_new_token",
139 HashMap::from([
140 ("tokenAddress", "a"),
141 ("name", "n"),
142 ("symbol", "s"),
143 ("decimals", "dec"),
144 ("imageUrl", "iu"),
145 ("description", "de"),
146 ("createdAtMs", "cts"),
147 ("coingeckoCoinId", "cgi"),
148 ("socialMedia.twitter", "sm.tw"),
149 ("socialMedia.telegram", "sm.tg"),
150 ("socialMedia.website", "sm.w"),
151 ("socialMedia.tiktok", "sm.tt"),
152 ("socialMedia.discord", "sm.dc"),
153 ("socialMedia.facebook", "sm.fb"),
154 ("socialMedia.github", "sm.gh"),
155 ("socialMedia.instagram", "sm.ig"),
156 ("socialMedia.linkedin", "sm.li"),
157 ("socialMedia.medium", "sm.md"),
158 ("socialMedia.reddit", "sm.rd"),
159 ("socialMedia.youtube", "sm.yt"),
160 ("socialMedia.bitbucket", "sm.bb"),
161 ("launchFrom.programAddress", "lf.pa"),
162 ("launchFrom.protocolFamily", "lf.pf"),
163 ("launchFrom.protocolName", "lf.pn"),
164 ("migratedTo.programAddress", "mt.pa"),
165 ("migratedTo.protocolFamily", "mt.pf"),
166 ("migratedTo.protocolName", "mt.pn"),
167 ]),
168 );
169
170 mappings.insert(
172 "subscribe_token_supply",
173 HashMap::from([("tokenAddress", "a"), ("supply", "s"), ("timestamp", "ts")]),
174 );
175
176 mappings.insert(
178 "subscribe_dex_pool_balance",
179 HashMap::from([
180 ("poolAddress", "a"),
181 ("tokenAAddress", "taa"),
182 ("tokenALiquidityInUsd", "taliu"),
183 ("tokenBAddress", "tba"),
184 ("tokenBLiquidityInUsd", "tbliu"),
185 ]),
186 );
187
188 mappings.insert(
190 "subscribe_token_liquidity",
191 HashMap::from([
192 ("tokenAddress", "a"),
193 ("metricType", "t"),
194 ("value", "v"),
195 ("timestamp", "ts"),
196 ]),
197 );
198
199 mappings.insert(
201 "subscribe_new_tokens_metadata",
202 HashMap::from([
203 ("tokenAddress", "a"),
204 ("name", "n"),
205 ("decimals", "dec"),
206 ("symbol", "s"),
207 ("imageUrl", "iu"),
208 ("description", "de"),
209 ("createdAtMs", "cts"),
210 ("coingeckoCoinId", "cgi"),
211 ("socialMedia.twitter", "sm.tw"),
212 ("socialMedia.telegram", "sm.tg"),
213 ("socialMedia.website", "sm.w"),
214 ("socialMedia.tiktok", "sm.tt"),
215 ("socialMedia.discord", "sm.dc"),
216 ("socialMedia.facebook", "sm.fb"),
217 ("socialMedia.github", "sm.gh"),
218 ("socialMedia.instagram", "sm.ig"),
219 ("socialMedia.linkedin", "sm.li"),
220 ("socialMedia.medium", "sm.md"),
221 ("socialMedia.reddit", "sm.rd"),
222 ("socialMedia.youtube", "sm.yt"),
223 ("socialMedia.bitbucket", "sm.bb"),
224 ("launchFrom.programAddress", "lf.pa"),
225 ("launchFrom.protocolFamily", "lf.pf"),
226 ("launchFrom.protocolName", "lf.pn"),
227 ("migratedTo.programAddress", "mt.pa"),
228 ("migratedTo.protocolFamily", "mt.pf"),
229 ("migratedTo.protocolName", "mt.pn"),
230 ]),
231 );
232
233 mappings.insert(
235 "subscribe_new_tokens",
236 HashMap::from([
237 ("tokenAddress", "a"),
238 ("name", "n"),
239 ("decimals", "dec"),
240 ("symbol", "s"),
241 ("imageUrl", "iu"),
242 ("description", "de"),
243 ("createdAtMs", "cts"),
244 ("coingeckoCoinId", "cgi"),
245 ("socialMedia.twitter", "sm.tw"),
246 ("socialMedia.telegram", "sm.tg"),
247 ("socialMedia.website", "sm.w"),
248 ("socialMedia.tiktok", "sm.tt"),
249 ("socialMedia.discord", "sm.dc"),
250 ("socialMedia.facebook", "sm.fb"),
251 ("socialMedia.github", "sm.gh"),
252 ("socialMedia.instagram", "sm.ig"),
253 ("socialMedia.linkedin", "sm.li"),
254 ("socialMedia.medium", "sm.md"),
255 ("socialMedia.reddit", "sm.rd"),
256 ("socialMedia.youtube", "sm.yt"),
257 ("socialMedia.bitbucket", "sm.bb"),
258 ("launchFrom.programAddress", "lf.pa"),
259 ("launchFrom.protocolFamily", "lf.pf"),
260 ("launchFrom.protocolName", "lf.pn"),
261 ("migratedTo.programAddress", "mt.pa"),
262 ("migratedTo.protocolFamily", "mt.pf"),
263 ("migratedTo.protocolName", "mt.pn"),
264 ]),
265 );
266
267 mappings.insert(
269 "subscribe_token_trades",
270 HashMap::from([
271 ("tokenAddress", "a"),
272 ("timestamp", "t"),
273 ("kind", "k"),
274 ("buyAmount", "ba"),
275 ("buyAmountInUsd", "baiu"),
276 ("buyTokenAddress", "btma"),
277 ("buyTokenName", "btn"),
278 ("buyTokenSymbol", "bts"),
279 ("buyWalletAddress", "bwa"),
280 ("sellAmount", "sa"),
281 ("sellAmountInUsd", "saiu"),
282 ("sellTokenAddress", "stma"),
283 ("sellTokenName", "stn"),
284 ("sellTokenSymbol", "sts"),
285 ("sellWalletAddress", "swa"),
286 ("txHash", "h"),
287 ]),
288 );
289
290 mappings.insert(
292 "subscribe_wallet_pnl",
293 HashMap::from([
294 ("walletAddress", "a"),
295 ("tokenAddress", "ta"),
296 ("tokenPriceInUsd", "tpiu"),
297 ("timestamp", "t"),
298 ("opentime", "ot"),
299 ("lasttime", "lt"),
300 ("closetime", "ct"),
301 ("buyAmount", "ba"),
302 ("buyAmountInUsd", "baiu"),
303 ("buyCount", "bs"),
304 ("buyCount30d", "bs30d"),
305 ("buyCount7d", "bs7d"),
306 ("sellAmount", "sa"),
307 ("sellAmountInUsd", "saiu"),
308 ("sellCount", "ss"),
309 ("sellCount30d", "ss30d"),
310 ("sellCount7d", "ss7d"),
311 ("heldDurationTimestamp", "hdts"),
312 ("averageBuyPriceInUsd", "abpiu"),
313 ("averageSellPriceInUsd", "aspiu"),
314 ("unrealizedProfitInUsd", "upiu"),
315 ("unrealizedProfitRatio", "upr"),
316 ("realizedProfitInUsd", "rpiu"),
317 ("realizedProfitRatio", "rpr"),
318 ("totalRealizedProfitInUsd", "trpiu"),
319 ("totalRealizedProfitRatio", "trr"),
320 ]),
321 );
322
323 mappings.insert(
325 "subscribe_wallet_trade",
326 HashMap::from([
327 ("tokenAddress", "a"),
328 ("timestamp", "t"),
329 ("kind", "k"),
330 ("buyAmount", "ba"),
331 ("buyAmountInUsd", "baiu"),
332 ("buyTokenAddress", "btma"),
333 ("buyTokenName", "btn"),
334 ("buyTokenSymbol", "bts"),
335 ("buyWalletAddress", "bwa"),
336 ("sellAmount", "sa"),
337 ("sellAmountInUsd", "saiu"),
338 ("sellTokenAddress", "stma"),
339 ("sellTokenName", "stn"),
340 ("sellTokenSymbol", "sts"),
341 ("sellWalletAddress", "swa"),
342 ("txHash", "h"),
343 ]),
344 );
345
346 mappings.insert(
348 "subscribe_token_max_liquidity",
349 HashMap::from([
350 ("tokenAddress", "a"),
351 ("poolAddress", "p"),
352 ("liquidityInUsd", "liu"),
353 ("liquidityInNative", "lin"),
354 ("timestamp", "ts"),
355 ]),
356 );
357
358 mappings.insert(
360 "subscribe_token_total_liquidity",
361 HashMap::from([
362 ("tokenAddress", "a"),
363 ("liquidityInUsd", "liu"),
364 ("liquidityInNative", "lin"),
365 ("poolCount", "pc"),
366 ("timestamp", "ts"),
367 ]),
368 );
369
370 mappings
371});
372
373pub fn get_field_mappings(method_name: &str) -> Option<&FieldMapping> {
375 CEL_FIELD_MAPPINGS.get(method_name)
376}
377
378pub fn replace_filter_fields(filter: &str, method_name: &str) -> String {
382 if filter.is_empty() {
383 return filter.to_string();
384 }
385
386 let field_mappings = match get_field_mappings(method_name) {
387 Some(m) => m,
388 None => return filter.to_string(),
389 };
390
391 let mut result = filter.to_string();
392
393 let mut entries: Vec<_> = field_mappings.iter().collect();
395 entries.sort_by(|a, b| b.0.len().cmp(&a.0.len()));
396
397 for (long_field, short_field) in entries {
398 let pattern1 = format!(r"\bmeta\.{}\b", regex::escape(long_field));
401 if let Ok(re) = regex::Regex::new(&pattern1) {
402 result = re
403 .replace_all(&result, format!("meta.{}", short_field))
404 .to_string();
405 }
406
407 let pattern2 = format!(r"\b{}\b", regex::escape(long_field));
409 if let Ok(re) = regex::Regex::new(&pattern2) {
410 result = re
411 .replace_all(&result, format!("meta.{}", short_field))
412 .to_string();
413 }
414 }
415
416 result
417}
418
419pub fn get_available_fields(method_name: &str) -> Vec<&'static str> {
421 match get_field_mappings(method_name) {
422 Some(mappings) => mappings.keys().copied().collect(),
423 None => Vec::new(),
424 }
425}
426
427#[cfg(test)]
428mod tests {
429 use super::*;
430
431 #[test]
432 fn test_get_field_mappings() {
433 let mappings = get_field_mappings("subscribe_token_candles");
434 assert!(mappings.is_some());
435 let mappings = mappings.unwrap();
436 assert_eq!(mappings.get("open"), Some(&"o"));
437 assert_eq!(mappings.get("close"), Some(&"c"));
438 }
439
440 #[test]
441 fn test_replace_filter_fields() {
442 let filter = "open > 100 && close < 200";
443 let result = replace_filter_fields(filter, "subscribe_token_candles");
444 assert!(result.contains("meta.o"));
445 assert!(result.contains("meta.c"));
446 }
447
448 #[test]
449 fn test_get_available_fields() {
450 let fields = get_available_fields("subscribe_token_candles");
451 assert!(fields.contains(&"open"));
452 assert!(fields.contains(&"close"));
453 assert!(fields.contains(&"high"));
454 assert!(fields.contains(&"low"));
455 }
456}