Skip to content

Commit

Permalink
feat(libs): add blockscout-chains crate (#1077)
Browse files Browse the repository at this point in the history
* feat: add blockscout-chains crate

* feat: add derives

* fix: change chain_id type

* feat: add builder

* chore: remove unused deps
  • Loading branch information
lok52 authored Oct 15, 2024
1 parent 7265e1c commit 096dfbe
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 0 deletions.
1 change: 1 addition & 0 deletions libs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
resolver = "2"
members = [
"blockscout-auth",
"blockscout-chains",
"blockscout-client/crate",
"blockscout-db",
"endpoints/swagger",
Expand Down
15 changes: 15 additions & 0 deletions libs/blockscout-chains/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "blockscout-chains"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.12", features = ["json"] }
reqwest-middleware = "0.3"
reqwest-retry = "0.6"

[dev-dependencies]
tokio = { version = "1.0", features = ["macros"] }
112 changes: 112 additions & 0 deletions libs/blockscout-chains/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
use serde::Deserialize;
use std::collections::HashMap;

const CHAINS_URL: &str = "https://chains.blockscout.com/api/chains";

pub struct BlockscoutChainsClient {
client: ClientWithMiddleware,
url: String,
}

impl BlockscoutChainsClient {
pub fn builder() -> BlockscoutChainsClientBuilder {
Default::default()
}

pub async fn fetch_all(&self) -> Result<BlockscoutChains, reqwest_middleware::Error> {
let res = self.client.get(&self.url).send().await?;
let chains: BlockscoutChains = res.json().await?;
Ok(chains)
}
}

impl Default for BlockscoutChainsClient {
fn default() -> Self {
Self::builder().build()
}
}

pub struct BlockscoutChainsClientBuilder {
max_retries: u32,
url: String,
}

impl BlockscoutChainsClientBuilder {
pub fn with_max_retries(mut self, max_retries: u32) -> Self {
self.max_retries = max_retries;
self
}

pub fn with_url(mut self, url: String) -> Self {
self.url = url;
self
}

pub fn build(self) -> BlockscoutChainsClient {
let retry_policy = ExponentialBackoff::builder().build_with_max_retries(self.max_retries);
let client = ClientBuilder::new(reqwest::Client::new())
.with(RetryTransientMiddleware::new_with_policy(retry_policy))
.build();
BlockscoutChainsClient {
client,
url: self.url,
}
}
}

impl Default for BlockscoutChainsClientBuilder {
fn default() -> Self {
Self {
url: CHAINS_URL.to_string(),
max_retries: 3,
}
}
}

pub type BlockscoutChains = HashMap<u64, BlockscoutChainData>;

#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct BlockscoutChainData {
pub name: String,
pub description: String,
pub ecosystem: Ecosystem,
pub is_testnet: Option<bool>,
pub layer: Option<u8>,
pub rollup_type: Option<String>,
pub website: String,
pub explorers: Vec<ExplorerConfig>,
pub logo: String,
}

#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(untagged)]
pub enum Ecosystem {
Single(String),
Multiple(Vec<String>),
}

#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ExplorerConfig {
pub url: String,
pub hosted_by: String,
}

#[cfg(test)]
mod tests {
use super::*;

#[tokio::test]
async fn test_get_blockscout_chains() {
let chains = BlockscoutChainsClient::builder()
.with_max_retries(0)
.build()
.fetch_all()
.await
.unwrap();
assert!(!chains.is_empty());
}
}

0 comments on commit 096dfbe

Please sign in to comment.