1use crate::github::Content;
2use eyre::Report;
3use http::Method;
4use serde::de::DeserializeOwned;
5
6pub use crate::{assets::*, chain::*, paths::*};
7
8#[cfg(all(feature = "registry-cache"))]
9pub use self::cache::*;
10
11
12const VERSION: &str = env!("CARGO_PKG_VERSION");
13const GIT_REF: &str = "d063b0fd6d1c20d6476880e5ea2212ade009f69e";
16const RAW_FILE_REPO_URL: &str = "https://raw.githubusercontent.com/cosmos/chain-registry";
17const REPO_URL: &str = "https://api.github.com/repos/cosmos/chain-registry/contents";
18
19async fn get(url: String) -> Result<String, Report> {
20 let client = reqwest::Client::new();
21 let req = client
22 .request(Method::GET, url)
23 .header("User-Agent", format!("ocular/{}", VERSION))
24 .build()?;
25 Ok(client.execute(req).await?.text().await?)
26}
27
28pub async fn list_chains() -> Result<Vec<String>, Report> {
30 let url = format!("{}?ref={}", REPO_URL, GIT_REF,);
31 let json: String = get(url).await?;
32 let contents: Vec<Content> = serde_json::from_str(json.as_str())?;
33
34 Ok(contents
35 .iter()
36 .filter(|c| c.type_field == "dir" && !c.name.starts_with('_') && c.name != ".github")
37 .map(|c| c.clone().name)
38 .collect())
39}
40
41pub async fn list_paths() -> Result<Vec<String>, Report> {
43 let url = format!("{}/_IBC?ref={}", REPO_URL, GIT_REF,);
44 let json: String = get(url).await?;
45 let contents: Vec<Content> = serde_json::from_str(json.as_str())?;
46
47 Ok(contents
48 .iter()
49 .filter(|c| c.type_field == "file" && !c.name.starts_with('_') && c.name.ends_with(".json"))
50 .map(|c| c.name[..c.name.len() - ".json".len()].to_string())
51 .collect())
52}
53
54pub async fn get_assets(name: &str) -> Result<Option<AssetList>, Report> {
62 let path = format!("{}/assetlist.json", name);
63 let data = get_file_content(GIT_REF, &path).await?;
64
65 Ok(parse_json(data).await)
66}
67
68pub async fn get_chain(name: &str) -> Result<Option<ChainInfo>, Report> {
76 let path = format!("{}/chain.json", name);
77 let data = get_file_content(GIT_REF, &path).await?;
78
79 Ok(parse_json(data).await)
80}
81
82pub async fn get_path(chain_a: &str, chain_b: &str) -> Result<Option<IBCPath>, Report> {
90 let path = format!(
92 "_IBC/{}-{}.json",
93 chain_a.min(chain_b),
94 chain_a.max(chain_b)
95 );
96 let data = get_file_content(GIT_REF, &path).await?;
97
98 Ok(parse_json(data).await)
99}
100
101async fn get_file_content(r#ref: &str, path: &str) -> Result<String, Report> {
102 let url = format!("{}/{}/{}", RAW_FILE_REPO_URL, r#ref, path);
103 Ok(reqwest::get(url).await?.text().await?)
104}
105
106async fn parse_json<T>(data: String) -> Option<T>
107where
108 T: DeserializeOwned,
109{
110 serde_json::from_str(&data).ok()
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use assay::assay;
117
118 #[assay]
119 async fn gets_content_from_registry() {
120 let result = get_file_content(GIT_REF, "cosmoshub/chain.json").await;
121
122 result.unwrap();
123 }
124
125 #[assay]
126 async fn parses_chain_info() {
127 let result = get_file_content(GIT_REF, "cosmoshub/chain.json")
128 .await
129 .unwrap();
130 let result = parse_json::<ChainInfo>(result).await;
131
132 result.unwrap();
133 }
134
135 #[assay]
136 async fn gets_chain() {
137 let result = get_chain("cosmoshub").await;
138
139 result.unwrap();
140 }
141
142 #[assay]
143 async fn lists_chains() {
144 list_chains().await.unwrap();
145 }
146
147 #[assay]
148 async fn lists_paths() {
149 let paths = list_paths().await.unwrap();
150 assert!(paths.len() > 0);
151 paths
152 .iter()
153 .for_each(|path| assert!(!path.ends_with(".json")))
154 }
155
156 #[assay]
157 async fn gets_path_in_order() {
158 let chain_a = "cosmoshub";
159 let chain_b = "osmosis";
160 let result = get_path(chain_a, chain_b).await.unwrap().unwrap();
161 assert_eq!(result.chain_1.chain_name, "cosmoshub");
162 assert_eq!(result.chain_2.chain_name, "osmosis");
163 }
164
165 #[assay]
166 async fn gets_path_out_of_order() {
167 let chain_a = "cosmoshub";
168 let chain_b = "osmosis";
169 let result = get_path(chain_b, chain_a).await.unwrap().unwrap();
170 assert_eq!(result.chain_1.chain_name, "cosmoshub");
171 assert_eq!(result.chain_2.chain_name, "osmosis");
172 }
173
174 #[assay]
175 async fn get_path_not_present_returns_none() {
176 let chain_a = "fake";
177 let chain_b = "osmosis";
178 let result = get_path(chain_b, chain_a).await.unwrap();
179 assert!(result.is_none())
180 }
181}