mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
refactor: move cli-only utils out of staged-sync (#2984)
This commit is contained in:
@ -24,4 +24,6 @@ mod stage_args;
|
||||
pub use stage_args::StageEnum;
|
||||
|
||||
mod gas_price_oracle_args;
|
||||
pub mod utils;
|
||||
|
||||
pub use gas_price_oracle_args::GasPriceOracleArgs;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::{utils::parse_duration_from_secs, version::default_extradata};
|
||||
use crate::{args::utils::parse_duration_from_secs, version::default_extradata};
|
||||
use clap::{
|
||||
builder::{RangedU64ValueParser, TypedValueParser},
|
||||
Arg, Args, Command,
|
||||
|
||||
138
bin/reth/src/args/utils.rs
Normal file
138
bin/reth/src/args/utils.rs
Normal file
@ -0,0 +1,138 @@
|
||||
//! Clap parser utilities
|
||||
|
||||
use reth_primitives::{AllGenesisFormats, BlockHashOrNumber, ChainSpec, GOERLI, MAINNET, SEPOLIA};
|
||||
use reth_revm::primitives::B256 as H256;
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs},
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
/// Helper to parse a [Duration] from seconds
|
||||
pub fn parse_duration_from_secs(arg: &str) -> eyre::Result<Duration, std::num::ParseIntError> {
|
||||
let seconds = arg.parse()?;
|
||||
Ok(Duration::from_secs(seconds))
|
||||
}
|
||||
|
||||
/// Clap value parser for [ChainSpec]s that takes either a built-in chainspec or the path
|
||||
/// to a custom one.
|
||||
pub fn chain_spec_value_parser(s: &str) -> eyre::Result<Arc<ChainSpec>, eyre::Error> {
|
||||
Ok(Arc::new(match s {
|
||||
"mainnet" => MAINNET.clone(),
|
||||
"goerli" => GOERLI.clone(),
|
||||
"sepolia" => SEPOLIA.clone(),
|
||||
_ => {
|
||||
let raw = std::fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?;
|
||||
serde_json::from_str(&raw)?
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
/// Clap value parser for [ChainSpec]s that takes either a built-in genesis format or the path
|
||||
/// to a custom one.
|
||||
pub fn genesis_value_parser(s: &str) -> eyre::Result<Arc<ChainSpec>, eyre::Error> {
|
||||
Ok(Arc::new(match s {
|
||||
"mainnet" => MAINNET.clone(),
|
||||
"goerli" => GOERLI.clone(),
|
||||
"sepolia" => SEPOLIA.clone(),
|
||||
_ => {
|
||||
let raw = std::fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?;
|
||||
let genesis: AllGenesisFormats = serde_json::from_str(&raw)?;
|
||||
genesis.into()
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
/// Parse [BlockHashOrNumber]
|
||||
pub fn hash_or_num_value_parser(value: &str) -> eyre::Result<BlockHashOrNumber, eyre::Error> {
|
||||
match H256::from_str(value) {
|
||||
Ok(hash) => Ok(BlockHashOrNumber::Hash(hash)),
|
||||
Err(_) => Ok(BlockHashOrNumber::Number(value.parse()?)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Error thrown while parsing a socket address.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum SocketAddressParsingError {
|
||||
/// Failed to convert the string into a socket addr
|
||||
#[error("Cannot parse socket address: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
/// Input must not be empty
|
||||
#[error("Cannot parse socket address from empty string")]
|
||||
Empty,
|
||||
/// Failed to parse the address
|
||||
#[error("Could not parse socket address from {0}")]
|
||||
Parse(String),
|
||||
/// Failed to parse port
|
||||
#[error("Could not parse port: {0}")]
|
||||
Port(#[from] std::num::ParseIntError),
|
||||
}
|
||||
|
||||
/// Parse a [SocketAddr] from a `str`.
|
||||
///
|
||||
/// The following formats are checked:
|
||||
///
|
||||
/// - If the value can be parsed as a `u16` or starts with `:` it is considered a port, and the
|
||||
/// hostname is set to `localhost`.
|
||||
/// - If the value contains `:` it is assumed to be the format `<host>:<port>`
|
||||
/// - Otherwise it is assumed to be a hostname
|
||||
///
|
||||
/// An error is returned if the value is empty.
|
||||
pub fn parse_socket_address(value: &str) -> eyre::Result<SocketAddr, SocketAddressParsingError> {
|
||||
if value.is_empty() {
|
||||
return Err(SocketAddressParsingError::Empty)
|
||||
}
|
||||
|
||||
if let Some(port) = value.strip_prefix(':').or_else(|| value.strip_prefix("localhost:")) {
|
||||
let port: u16 = port.parse()?;
|
||||
return Ok(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port))
|
||||
}
|
||||
if let Ok(port) = value.parse::<u16>() {
|
||||
return Ok(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port))
|
||||
}
|
||||
value
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.ok_or_else(|| SocketAddressParsingError::Parse(value.to_string()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use proptest::prelude::Rng;
|
||||
use secp256k1::rand::thread_rng;
|
||||
|
||||
#[test]
|
||||
fn parse_chain_spec() {
|
||||
for chain in ["mainnet", "sepolia", "goerli"] {
|
||||
chain_spec_value_parser(chain).unwrap();
|
||||
genesis_value_parser(chain).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_socket_addresses() {
|
||||
for value in ["localhost:9000", ":9000", "9000"] {
|
||||
let socket_addr = parse_socket_address(value)
|
||||
.unwrap_or_else(|_| panic!("could not parse socket address: {value}"));
|
||||
|
||||
assert!(socket_addr.ip().is_loopback());
|
||||
assert_eq!(socket_addr.port(), 9000);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_socket_address_random() {
|
||||
let port: u16 = thread_rng().gen();
|
||||
|
||||
for value in [format!("localhost:{port}"), format!(":{port}"), port.to_string()] {
|
||||
let socket_addr = parse_socket_address(&value)
|
||||
.unwrap_or_else(|_| panic!("could not parse socket address: {value}"));
|
||||
|
||||
assert!(socket_addr.ip().is_loopback());
|
||||
assert_eq!(socket_addr.port(), port);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ use eyre::Context;
|
||||
use futures::{Stream, StreamExt};
|
||||
use reth_beacon_consensus::BeaconConsensus;
|
||||
|
||||
use crate::args::utils::genesis_value_parser;
|
||||
use reth_config::Config;
|
||||
use reth_db::database::Database;
|
||||
use reth_downloaders::{
|
||||
@ -16,10 +17,7 @@ use reth_downloaders::{
|
||||
};
|
||||
use reth_interfaces::consensus::Consensus;
|
||||
use reth_primitives::{ChainSpec, H256};
|
||||
use reth_staged_sync::utils::{
|
||||
chainspec::genesis_value_parser,
|
||||
init::{init_db, init_genesis},
|
||||
};
|
||||
use reth_staged_sync::utils::init::{init_db, init_genesis};
|
||||
use reth_stages::{
|
||||
prelude::*,
|
||||
stages::{
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
use crate::dirs::{DataDirPath, MaybePlatformPath};
|
||||
use crate::{
|
||||
args::utils::genesis_value_parser,
|
||||
dirs::{DataDirPath, MaybePlatformPath},
|
||||
};
|
||||
use clap::Parser;
|
||||
use reth_primitives::ChainSpec;
|
||||
use reth_staged_sync::utils::{
|
||||
chainspec::genesis_value_parser,
|
||||
init::{init_db, init_genesis},
|
||||
};
|
||||
use reth_staged_sync::utils::init::{init_db, init_genesis};
|
||||
use std::sync::Arc;
|
||||
use tracing::info;
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
//! Database debugging tool
|
||||
use crate::{
|
||||
args::utils::genesis_value_parser,
|
||||
dirs::{DataDirPath, MaybePlatformPath},
|
||||
utils::DbTool,
|
||||
};
|
||||
@ -9,7 +10,6 @@ use eyre::WrapErr;
|
||||
use human_bytes::human_bytes;
|
||||
use reth_db::{database::Database, tables};
|
||||
use reth_primitives::ChainSpec;
|
||||
use reth_staged_sync::utils::chainspec::genesis_value_parser;
|
||||
use std::sync::Arc;
|
||||
use tracing::error;
|
||||
|
||||
|
||||
@ -27,10 +27,9 @@ use reth_network::NetworkHandle;
|
||||
use reth_network_api::NetworkInfo;
|
||||
use reth_primitives::{stage::StageId, BlockHashOrNumber, BlockNumber, ChainSpec, H256};
|
||||
use reth_provider::{providers::get_stage_checkpoint, ShareableDatabase, Transaction};
|
||||
use reth_staged_sync::utils::{
|
||||
chainspec::genesis_value_parser,
|
||||
init::{init_db, init_genesis},
|
||||
};
|
||||
use reth_staged_sync::utils::init::{init_db, init_genesis};
|
||||
|
||||
use crate::args::utils::genesis_value_parser;
|
||||
use reth_stages::{
|
||||
sets::DefaultStages,
|
||||
stages::{
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
//! Command for debugging merkle trie calculation.
|
||||
use crate::dirs::{DataDirPath, MaybePlatformPath};
|
||||
use crate::{
|
||||
args::utils::genesis_value_parser,
|
||||
dirs::{DataDirPath, MaybePlatformPath},
|
||||
};
|
||||
use clap::Parser;
|
||||
use reth_db::{cursor::DbCursorRO, tables, transaction::DbTx};
|
||||
use reth_primitives::{
|
||||
@ -7,7 +10,7 @@ use reth_primitives::{
|
||||
ChainSpec,
|
||||
};
|
||||
use reth_provider::Transaction;
|
||||
use reth_staged_sync::utils::{chainspec::genesis_value_parser, init::init_db};
|
||||
use reth_staged_sync::utils::init::init_db;
|
||||
use reth_stages::{
|
||||
stages::{
|
||||
AccountHashingStage, ExecutionStage, ExecutionStageThresholds, MerkleStage,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
//! reth data directories.
|
||||
use crate::utils::parse_path;
|
||||
use reth_primitives::Chain;
|
||||
use reth_staged_sync::utils::parse_path;
|
||||
use std::{
|
||||
env::VarError,
|
||||
fmt::{Debug, Display, Formatter},
|
||||
|
||||
@ -51,11 +51,7 @@ use reth_provider::{
|
||||
use reth_revm::Factory;
|
||||
use reth_revm_inspectors::stack::Hook;
|
||||
use reth_rpc_engine_api::EngineApi;
|
||||
use reth_staged_sync::utils::{
|
||||
chainspec::genesis_value_parser,
|
||||
init::{init_db, init_genesis},
|
||||
parse_socket_address,
|
||||
};
|
||||
use reth_staged_sync::utils::init::{init_db, init_genesis};
|
||||
use reth_stages::{
|
||||
prelude::*,
|
||||
stages::{
|
||||
@ -74,7 +70,13 @@ use std::{
|
||||
use tokio::sync::{mpsc::unbounded_channel, oneshot, watch};
|
||||
use tracing::*;
|
||||
|
||||
use crate::{args::PayloadBuilderArgs, dirs::MaybePlatformPath};
|
||||
use crate::{
|
||||
args::{
|
||||
utils::{genesis_value_parser, parse_socket_address},
|
||||
PayloadBuilderArgs,
|
||||
},
|
||||
dirs::MaybePlatformPath,
|
||||
};
|
||||
use reth_interfaces::p2p::headers::client::HeadersClient;
|
||||
use reth_payload_builder::PayloadBuilderService;
|
||||
use reth_primitives::bytes::BytesMut;
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
//! P2P Debugging tool
|
||||
use crate::{
|
||||
args::{get_secret_key, DiscoveryArgs},
|
||||
args::{
|
||||
get_secret_key,
|
||||
utils::{chain_spec_value_parser, hash_or_num_value_parser},
|
||||
DiscoveryArgs,
|
||||
},
|
||||
dirs::{DataDirPath, MaybePlatformPath},
|
||||
utils::get_single_header,
|
||||
};
|
||||
@ -12,7 +16,6 @@ use reth_discv4::NatResolver;
|
||||
use reth_interfaces::p2p::bodies::client::BodiesClient;
|
||||
use reth_primitives::{BlockHashOrNumber, ChainSpec, NodeRecord};
|
||||
use reth_provider::ShareableDatabase;
|
||||
use reth_staged_sync::utils::{chainspec::chain_spec_value_parser, hash_or_num_value_parser};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
/// `reth p2p` command
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
//! Database debugging tool
|
||||
use crate::{
|
||||
args::StageEnum,
|
||||
args::{utils::genesis_value_parser, StageEnum},
|
||||
dirs::{DataDirPath, MaybePlatformPath},
|
||||
utils::DbTool,
|
||||
};
|
||||
@ -12,7 +12,7 @@ use reth_db::{
|
||||
transaction::DbTxMut,
|
||||
};
|
||||
use reth_primitives::{stage::StageId, ChainSpec};
|
||||
use reth_staged_sync::utils::{chainspec::genesis_value_parser, init::insert_genesis_state};
|
||||
use reth_staged_sync::utils::init::insert_genesis_state;
|
||||
use std::sync::Arc;
|
||||
use tracing::info;
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ use reth_db::{
|
||||
cursor::DbCursorRO, database::Database, table::TableImporter, tables, transaction::DbTx,
|
||||
};
|
||||
use reth_primitives::ChainSpec;
|
||||
use reth_staged_sync::utils::{chainspec::genesis_value_parser, init::init_db};
|
||||
use reth_staged_sync::utils::init::init_db;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use tracing::info;
|
||||
|
||||
@ -22,6 +22,7 @@ mod execution;
|
||||
use execution::dump_execution_stage;
|
||||
|
||||
mod merkle;
|
||||
use crate::args::utils::genesis_value_parser;
|
||||
use merkle::dump_merkle_stage;
|
||||
|
||||
/// `reth dump-stage` command
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! Stage debugging tool
|
||||
use crate::{
|
||||
args::{get_secret_key, NetworkArgs, StageEnum},
|
||||
args::{get_secret_key, utils::chain_spec_value_parser, NetworkArgs, StageEnum},
|
||||
dirs::{DataDirPath, MaybePlatformPath},
|
||||
prometheus_exporter,
|
||||
version::SHORT_VERSION,
|
||||
@ -16,7 +16,7 @@ use reth_primitives::{
|
||||
ChainSpec,
|
||||
};
|
||||
use reth_provider::{ShareableDatabase, Transaction};
|
||||
use reth_staged_sync::utils::{chainspec::chain_spec_value_parser, init::init_db};
|
||||
use reth_staged_sync::utils::init::init_db;
|
||||
use reth_stages::{
|
||||
stages::{
|
||||
BodyStage, ExecutionStage, ExecutionStageThresholds, IndexAccountHistoryStage,
|
||||
|
||||
@ -10,9 +10,9 @@ use reth_db::{
|
||||
};
|
||||
use reth_primitives::{BlockHashOrNumber, ChainSpec};
|
||||
use reth_provider::Transaction;
|
||||
use reth_staged_sync::utils::chainspec::genesis_value_parser;
|
||||
use std::{ops::RangeInclusive, sync::Arc};
|
||||
|
||||
use crate::args::utils::genesis_value_parser;
|
||||
use reth_db::cursor::DbCursorRO;
|
||||
|
||||
/// `reth stage unwind` command
|
||||
|
||||
@ -12,14 +12,17 @@ use reth_interfaces::p2p::{
|
||||
priority::Priority,
|
||||
};
|
||||
use reth_primitives::{BlockHashOrNumber, HeadersDirection, SealedHeader};
|
||||
use std::{path::Path, time::Duration};
|
||||
use std::{
|
||||
env::VarError,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
/// Get a single header from network
|
||||
pub async fn get_single_header<Client>(
|
||||
client: Client,
|
||||
id: BlockHashOrNumber,
|
||||
) -> eyre::Result<SealedHeader>
|
||||
) -> Result<SealedHeader>
|
||||
where
|
||||
Client: HeadersClient,
|
||||
{
|
||||
@ -104,8 +107,8 @@ impl<'a, DB: Database> DbTool<'a, DB> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to parse a [Duration] from seconds
|
||||
pub fn parse_duration_from_secs(arg: &str) -> Result<Duration, std::num::ParseIntError> {
|
||||
let seconds = arg.parse()?;
|
||||
Ok(Duration::from_secs(seconds))
|
||||
/// Parses a user-specified path with support for environment variables and common shorthands (e.g.
|
||||
/// ~ for the user's home directory).
|
||||
pub fn parse_path(value: &str) -> Result<PathBuf, shellexpand::LookupError<VarError>> {
|
||||
shellexpand::full(value).map(|path| PathBuf::from(path.into_owned()))
|
||||
}
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
use reth_primitives::{AllGenesisFormats, ChainSpec, GOERLI, MAINNET, SEPOLIA};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
/// Clap value parser for [ChainSpec]s that takes either a built-in chainspec or the path
|
||||
/// to a custom one.
|
||||
pub fn chain_spec_value_parser(s: &str) -> Result<Arc<ChainSpec>, eyre::Error> {
|
||||
Ok(Arc::new(match s {
|
||||
"mainnet" => MAINNET.clone(),
|
||||
"goerli" => GOERLI.clone(),
|
||||
"sepolia" => SEPOLIA.clone(),
|
||||
_ => {
|
||||
let raw = std::fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?;
|
||||
serde_json::from_str(&raw)?
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
/// Clap value parser for [ChainSpec]s that takes either a built-in genesis format or the path
|
||||
/// to a custom one.
|
||||
pub fn genesis_value_parser(s: &str) -> Result<Arc<ChainSpec>, eyre::Error> {
|
||||
Ok(Arc::new(match s {
|
||||
"mainnet" => MAINNET.clone(),
|
||||
"goerli" => GOERLI.clone(),
|
||||
"sepolia" => SEPOLIA.clone(),
|
||||
_ => {
|
||||
let raw = std::fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?;
|
||||
let genesis: AllGenesisFormats = serde_json::from_str(&raw)?;
|
||||
genesis.into()
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_chain_spec() {
|
||||
for chain in ["mainnet", "sepolia", "goerli"] {
|
||||
chain_spec_value_parser(chain).unwrap();
|
||||
genesis_value_parser(chain).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,114 +1,4 @@
|
||||
//! Utility functions.
|
||||
use reth_primitives::{BlockHashOrNumber, H256};
|
||||
use std::{
|
||||
env::VarError,
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs},
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
/// Utilities for parsing chainspecs
|
||||
pub mod chainspec;
|
||||
|
||||
/// Utilities for initializing parts of the chain
|
||||
pub mod init;
|
||||
|
||||
/// Finds all files in a directory with a given postfix.
|
||||
pub fn find_all_files_with_postfix(path: &Path, postfix: &str) -> Vec<PathBuf> {
|
||||
WalkDir::new(path)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.file_name().to_string_lossy().ends_with(postfix))
|
||||
.map(DirEntry::into_path)
|
||||
.collect::<Vec<PathBuf>>()
|
||||
}
|
||||
|
||||
/// Parses a user-specified path with support for environment variables and common shorthands (e.g.
|
||||
/// ~ for the user's home directory).
|
||||
pub fn parse_path(value: &str) -> Result<PathBuf, shellexpand::LookupError<VarError>> {
|
||||
shellexpand::full(value).map(|path| PathBuf::from(path.into_owned()))
|
||||
}
|
||||
|
||||
/// Parse [BlockHashOrNumber]
|
||||
pub fn hash_or_num_value_parser(value: &str) -> Result<BlockHashOrNumber, eyre::Error> {
|
||||
match H256::from_str(value) {
|
||||
Ok(hash) => Ok(BlockHashOrNumber::Hash(hash)),
|
||||
Err(_) => Ok(BlockHashOrNumber::Number(value.parse()?)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Error thrown while parsing a socket address.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum SocketAddressParsingError {
|
||||
/// Failed to convert the string into a socket addr
|
||||
#[error("Cannot parse socket address: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
/// Input must not be empty
|
||||
#[error("Cannot parse socket address from empty string")]
|
||||
Empty,
|
||||
/// Failed to parse the address
|
||||
#[error("Could not parse socket address from {0}")]
|
||||
Parse(String),
|
||||
/// Failed to parse port
|
||||
#[error("Could not parse port: {0}")]
|
||||
Port(#[from] std::num::ParseIntError),
|
||||
}
|
||||
|
||||
/// Parse a [SocketAddr] from a `str`.
|
||||
///
|
||||
/// The following formats are checked:
|
||||
///
|
||||
/// - If the value can be parsed as a `u16` or starts with `:` it is considered a port, and the
|
||||
/// hostname is set to `localhost`.
|
||||
/// - If the value contains `:` it is assumed to be the format `<host>:<port>`
|
||||
/// - Otherwise it is assumed to be a hostname
|
||||
///
|
||||
/// An error is returned if the value is empty.
|
||||
pub fn parse_socket_address(value: &str) -> Result<SocketAddr, SocketAddressParsingError> {
|
||||
if value.is_empty() {
|
||||
return Err(SocketAddressParsingError::Empty)
|
||||
}
|
||||
|
||||
if let Some(port) = value.strip_prefix(':').or_else(|| value.strip_prefix("localhost:")) {
|
||||
let port: u16 = port.parse()?;
|
||||
return Ok(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port))
|
||||
}
|
||||
if let Ok(port) = value.parse::<u16>() {
|
||||
return Ok(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port))
|
||||
}
|
||||
value
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.ok_or_else(|| SocketAddressParsingError::Parse(value.to_string()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
#[test]
|
||||
fn parse_socket_addresses() {
|
||||
for value in ["localhost:9000", ":9000", "9000"] {
|
||||
let socket_addr = parse_socket_address(value)
|
||||
.unwrap_or_else(|_| panic!("could not parse socket address: {value}"));
|
||||
|
||||
assert!(socket_addr.ip().is_loopback());
|
||||
assert_eq!(socket_addr.port(), 9000);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_socket_address_random() {
|
||||
let port: u16 = thread_rng().gen();
|
||||
|
||||
for value in [format!("localhost:{port}"), format!(":{port}"), port.to_string()] {
|
||||
let socket_addr = parse_socket_address(&value)
|
||||
.unwrap_or_else(|_| panic!("could not parse socket address: {value}"));
|
||||
|
||||
assert!(socket_addr.ip().is_loopback());
|
||||
assert_eq!(socket_addr.port(), port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user