use std::time::Duration;
#[derive(Debug, Clone)]
pub struct ProviderConfig {
pub url: String,
pub rate_limit_per_second: Option<u32>,
pub timeout: Option<Duration>,
pub min_delay: Option<Duration>,
}
impl ProviderConfig {
#[must_use]
pub fn new(url: impl Into<String>) -> Self {
Self {
url: url.into(),
rate_limit_per_second: None,
timeout: None,
min_delay: None,
}
}
#[must_use]
pub fn with_rate_limit(mut self, requests_per_second: u32) -> Self {
self.rate_limit_per_second = Some(requests_per_second);
self
}
#[must_use]
pub fn with_rate_limit_opt(mut self, requests_per_second: Option<u32>) -> Self {
self.rate_limit_per_second = requests_per_second;
self
}
#[must_use]
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
#[must_use]
pub fn with_min_delay(mut self, delay: Duration) -> Self {
self.min_delay = Some(delay);
self
}
#[must_use]
pub fn has_rate_limiting(&self) -> bool {
self.rate_limit_per_second.is_some() || self.min_delay.is_some()
}
}
impl Default for ProviderConfig {
fn default() -> Self {
Self::new("http://localhost:8545")
}
}
impl ProviderConfig {
#[must_use]
pub fn public_endpoint(url: impl Into<String>) -> Self {
Self::new(url)
.with_rate_limit(5) .with_timeout(Duration::from_secs(30))
}
#[must_use]
pub fn private_endpoint(url: impl Into<String>) -> Self {
Self::new(url)
.with_rate_limit(50)
.with_timeout(Duration::from_secs(60))
}
#[must_use]
pub fn local_node(url: impl Into<String>) -> Self {
Self::new(url).with_timeout(Duration::from_secs(120))
}
#[must_use]
pub fn infura(project_id: &str, network: &str) -> Self {
Self::private_endpoint(format!("https://{network}.infura.io/v3/{project_id}"))
}
#[must_use]
pub fn alchemy(api_key: &str, network: &str) -> Self {
Self::private_endpoint(format!("https://{network}.g.alchemy.com/v2/{api_key}"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_provider_config_new() {
let config = ProviderConfig::new("https://eth.llamarpc.com");
assert_eq!(config.url, "https://eth.llamarpc.com");
assert!(config.rate_limit_per_second.is_none());
}
#[test]
fn test_provider_config_with_rate_limit() {
let config = ProviderConfig::new("https://eth.llamarpc.com").with_rate_limit(10);
assert_eq!(config.rate_limit_per_second, Some(10));
assert!(config.has_rate_limiting());
}
#[test]
fn test_provider_config_public_endpoint() {
let config = ProviderConfig::public_endpoint("https://eth.llamarpc.com");
assert_eq!(config.rate_limit_per_second, Some(5));
assert!(config.timeout.is_some());
}
#[test]
fn test_provider_config_private_endpoint() {
let config = ProviderConfig::private_endpoint("https://my-node.com");
assert_eq!(config.rate_limit_per_second, Some(50));
}
#[test]
fn test_provider_config_local_node() {
let config = ProviderConfig::local_node("http://localhost:8545");
assert!(config.rate_limit_per_second.is_none());
assert!(!config.has_rate_limiting());
}
#[test]
fn test_provider_config_infura() {
let config = ProviderConfig::infura("my-project-id", "mainnet");
assert!(config.url.contains("infura.io"));
assert!(config.url.contains("my-project-id"));
assert!(config.url.contains("mainnet"));
}
#[test]
fn test_provider_config_alchemy() {
let config = ProviderConfig::alchemy("my-api-key", "eth-mainnet");
assert!(config.url.contains("alchemy.com"));
assert!(config.url.contains("my-api-key"));
}
}