Skip to main content

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}