From ea8c2318345a4d685914a0b8a2a2143b0f571d02 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sun, 22 Jan 2023 22:08:17 +0100 Subject: [PATCH] fix: ensure urls have trailing / (#2069) * fix: ensure urls have trailing / * chore: rustfmt --- ethers-etherscan/src/lib.rs | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/ethers-etherscan/src/lib.rs b/ethers-etherscan/src/lib.rs index f8340d41..399d7f80 100644 --- a/ethers-etherscan/src/lib.rs +++ b/ethers-etherscan/src/lib.rs @@ -285,7 +285,7 @@ impl ClientBuilder { /// /// Fails if the `etherscan_url` is not a valid `Url` pub fn with_url(mut self, etherscan_url: impl IntoUrl) -> Result { - self.etherscan_url = Some(etherscan_url.into_url()?); + self.etherscan_url = Some(ensure_url(etherscan_url)?); Ok(self) } @@ -301,7 +301,7 @@ impl ClientBuilder { /// /// Fails if the `etherscan_api_url` is not a valid `Url` pub fn with_api_url(mut self, etherscan_api_url: impl IntoUrl) -> Result { - self.etherscan_api_url = Some(etherscan_api_url.into_url()?); + self.etherscan_api_url = Some(ensure_url(etherscan_api_url)?); Ok(self) } @@ -441,6 +441,25 @@ struct Query<'a, T: Serialize> { other: T, } +/// Ensures that the url is well formatted to be used by the Client's functions that join paths. +fn ensure_url(url: impl IntoUrl) -> std::result::Result { + let url_str = url.as_str(); + + // ensure URL ends with `/` + if url_str.ends_with('/') { + url.into_url() + } else { + into_url(format!("{url_str}/")) + } +} + +/// This is a hack to work around `IntoUrl`'s sealed private functions, which can't be called +/// normally. +#[inline] +fn into_url(url: impl IntoUrl) -> std::result::Result { + url.into_url() +} + #[cfg(test)] mod tests { use crate::{Client, EtherscanError}; @@ -450,6 +469,14 @@ mod tests { time::{Duration, SystemTime}, }; + #[test] + fn test_api_paths() { + let client = Client::new(Chain::Goerli, "").unwrap(); + assert_eq!(client.etherscan_api_url.as_str(), "https://api-goerli.etherscan.io/api/"); + + assert_eq!(client.block_url(100), "https://goerli.etherscan.io/block/100"); + } + #[test] fn chain_not_supported() { let err = Client::new_from_env(Chain::Morden).unwrap_err();