chain_registry/cache.rs
1#![cfg(feature = "cache")]
2/// Provides caching of registry data for easy querying and filtering.
3use crate::{paths::{IBCPath, Tag}, get::*};
4use eyre::Report;
5use serde::{Deserialize, Serialize};
6use std::{cmp::Ordering, collections::HashMap};
7
8// TO-DO:
9// - Option to load from local repo clone
10// - Currently don't see a need to cache chain/asset info but might need it in the future
11/// Used to cache IBC path data from the chain registry for easy filtering.
12#[derive(Default, Deserialize, Serialize)]
13pub struct RegistryCache {
14 paths: HashMap<String, IBCPath>,
15}
16
17impl RegistryCache {
18 /// Returns a cached [`IBCPath`] representing a channel between `chain_a` and `chain_b` if it exists.
19 /// Passing in the same value for `chain_a` and `chain_b` will always return `Ok(None)`.
20 ///
21 /// # Arguments
22 ///
23 /// * `chain_a` - A chain name. Must match a directory name in the root of the chain registry repository `<https://github.com/cosmos/chain-registry>`
24 /// * `chain_b` - A chain name. Must match a directory name in the root of the chain registry repository `<https://github.com/cosmos/chain-registry>`
25 pub async fn get_path(
26 &self,
27 chain_a: &str,
28 chain_b: &str,
29 ) -> Result<Option<IBCPath>, Report> {
30 let path_name = match chain_a.cmp(chain_b) {
31 Ordering::Less => chain_a.to_string() + "-" + chain_b,
32 Ordering::Equal => return Ok(None),
33 Ordering::Greater => chain_b.to_string() + "-" + chain_a,
34 };
35
36 Ok(self.paths.get(&path_name).cloned())
37 }
38
39 /// Returns cached [`IBCPath`] that match a provided [`Tag`]
40 ///
41 /// # Arguments
42 ///
43 /// * `tag` - A [`Tag`] representing the the desired key/value pair to filter by.
44 ///
45 /// # Examples
46 ///
47 /// ```ignore
48 /// use chain_registry::cache::{RegistryCache, Tag};
49 ///
50 /// // store paths from the registry repository in a cache
51 /// let cache = RegistryCache::try_new().await?;
52 /// let dex = "osmosis".to_string();
53 ///
54 /// // paths will contain a vec of all IBC paths containing the tag dex:osmosis
55 /// let paths = cache.get_paths_filtered(Tag::Dex(dex))?;
56 /// ```
57 pub async fn get_paths_filtered(&self, tag: Tag) -> Result<Vec<IBCPath>, Report> {
58 Ok(self
59 .paths
60 .iter()
61 .filter(|kv| match &tag {
62 Tag::Dex(d) => kv.1.channels[0].tags.dex.eq(d),
63 Tag::Preferred(p) => kv.1.channels[0].tags.preferred.eq(p),
64 Tag::Properties(p) => kv.1.channels[0].tags.properties.eq(p),
65 Tag::Status(s) => kv.1.channels[0].tags.status.eq(s),
66 })
67 .map(|kv| kv.1.to_owned())
68 .collect())
69 }
70
71 /// Creates a new cache by retrieving and deserializing each [`IBCPath`] from the Cosmos Chain Registry for easy filtering
72 pub async fn try_new() -> Result<RegistryCache, Report> {
73 let path_names = list_paths().await?;
74 let mut paths = HashMap::<String, IBCPath>::default();
75
76 for pn in path_names {
77 let cn: Vec<&str> = pn.split('-').collect();
78
79 // this unwrap is safe becauase we query the path directly from the list of path .json file names
80 // retrieved earlier, therefore the Option returned should never be None.
81 paths.insert(
82 pn.clone(),
83 get_path(cn[0], cn[1])
84 .await?
85 .expect("path returned None"),
86 );
87 }
88
89 Ok(RegistryCache { paths })
90 }
91}