use crate::github::Content;
use eyre::Report;
use http::Method;
use serde::de::DeserializeOwned;
pub use crate::{assets::*, chain::*, paths::*};
#[cfg(all(feature = "registry-cache"))]
pub use self::cache::*;
const VERSION: &str = env!("CARGO_PKG_VERSION");
const GIT_REF: &str = "d063b0fd6d1c20d6476880e5ea2212ade009f69e";
const RAW_FILE_REPO_URL: &str = "https://raw.githubusercontent.com/cosmos/chain-registry";
const REPO_URL: &str = "https://api.github.com/repos/cosmos/chain-registry/contents";
async fn get(url: String) -> Result<String, Report> {
let client = reqwest::Client::new();
let req = client
.request(Method::GET, url)
.header("User-Agent", format!("ocular/{}", VERSION))
.build()?;
Ok(client.execute(req).await?.text().await?)
}
pub async fn list_chains() -> Result<Vec<String>, Report> {
let url = format!("{}?ref={}", REPO_URL, GIT_REF,);
let json: String = get(url).await?;
let contents: Vec<Content> = serde_json::from_str(json.as_str())?;
Ok(contents
.iter()
.filter(|c| c.type_field == "dir" && !c.name.starts_with('_') && c.name != ".github")
.map(|c| c.clone().name)
.collect())
}
pub async fn list_paths() -> Result<Vec<String>, Report> {
let url = format!("{}/_IBC?ref={}", REPO_URL, GIT_REF,);
let json: String = get(url).await?;
let contents: Vec<Content> = serde_json::from_str(json.as_str())?;
Ok(contents
.iter()
.filter(|c| c.type_field == "file" && !c.name.starts_with('_') && c.name.ends_with(".json"))
.map(|c| c.name[..c.name.len() - ".json".len()].to_string())
.collect())
}
pub async fn get_assets(name: &str) -> Result<Option<AssetList>, Report> {
let path = format!("{}/assetlist.json", name);
let data = get_file_content(GIT_REF, &path).await?;
Ok(parse_json(data).await)
}
pub async fn get_chain(name: &str) -> Result<Option<ChainInfo>, Report> {
let path = format!("{}/chain.json", name);
let data = get_file_content(GIT_REF, &path).await?;
Ok(parse_json(data).await)
}
pub async fn get_path(chain_a: &str, chain_b: &str) -> Result<Option<IBCPath>, Report> {
let path = format!(
"_IBC/{}-{}.json",
chain_a.min(chain_b),
chain_a.max(chain_b)
);
let data = get_file_content(GIT_REF, &path).await?;
Ok(parse_json(data).await)
}
async fn get_file_content(r#ref: &str, path: &str) -> Result<String, Report> {
let url = format!("{}/{}/{}", RAW_FILE_REPO_URL, r#ref, path);
Ok(reqwest::get(url).await?.text().await?)
}
async fn parse_json<T>(data: String) -> Option<T>
where
T: DeserializeOwned,
{
serde_json::from_str(&data).ok()
}
#[cfg(test)]
mod tests {
use super::*;
use assay::assay;
#[assay]
async fn gets_content_from_registry() {
let result = get_file_content(GIT_REF, "cosmoshub/chain.json").await;
result.unwrap();
}
#[assay]
async fn parses_chain_info() {
let result = get_file_content(GIT_REF, "cosmoshub/chain.json")
.await
.unwrap();
let result = parse_json::<ChainInfo>(result).await;
result.unwrap();
}
#[assay]
async fn gets_chain() {
let result = get_chain("cosmoshub").await;
result.unwrap();
}
#[assay]
async fn lists_chains() {
list_chains().await.unwrap();
}
#[assay]
async fn lists_paths() {
let paths = list_paths().await.unwrap();
assert!(paths.len() > 0);
paths
.iter()
.for_each(|path| assert!(!path.ends_with(".json")))
}
#[assay]
async fn gets_path_in_order() {
let chain_a = "cosmoshub";
let chain_b = "osmosis";
let result = get_path(chain_a, chain_b).await.unwrap().unwrap();
assert_eq!(result.chain_1.chain_name, "cosmoshub");
assert_eq!(result.chain_2.chain_name, "osmosis");
}
#[assay]
async fn gets_path_out_of_order() {
let chain_a = "cosmoshub";
let chain_b = "osmosis";
let result = get_path(chain_b, chain_a).await.unwrap().unwrap();
assert_eq!(result.chain_1.chain_name, "cosmoshub");
assert_eq!(result.chain_2.chain_name, "osmosis");
}
#[assay]
async fn get_path_not_present_returns_none() {
let chain_a = "fake";
let chain_b = "osmosis";
let result = get_path(chain_b, chain_a).await.unwrap();
assert!(result.is_none())
}
}