1use alloy_eips::BlockHashOrNumber;
2use alloy_primitives::B256;
3use reth_fs_util::FsPathError;
4use std::{
5 net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs},
6 path::Path,
7 str::FromStr,
8 time::Duration,
9};
10
11pub fn parse_duration_from_secs(arg: &str) -> eyre::Result<Duration, std::num::ParseIntError> {
13 let seconds = arg.parse()?;
14 Ok(Duration::from_secs(seconds))
15}
16
17pub fn parse_duration_from_secs_or_ms(
23 arg: &str,
24) -> eyre::Result<Duration, std::num::ParseIntError> {
25 if arg.ends_with("ms") {
26 arg.trim_end_matches("ms").parse().map(Duration::from_millis)
27 } else if arg.ends_with('s') {
28 arg.trim_end_matches('s').parse().map(Duration::from_secs)
29 } else {
30 arg.parse().map(Duration::from_secs)
31 }
32}
33
34pub fn format_duration_as_secs_or_ms(duration: Duration) -> String {
37 if duration.as_millis().is_multiple_of(1000) {
38 format!("{}", duration.as_secs())
39 } else {
40 format!("{}ms", duration.as_millis())
41 }
42}
43
44pub fn hash_or_num_value_parser(value: &str) -> eyre::Result<BlockHashOrNumber, eyre::Error> {
46 match B256::from_str(value) {
47 Ok(hash) => Ok(BlockHashOrNumber::Hash(hash)),
48 Err(_) => Ok(BlockHashOrNumber::Number(value.parse()?)),
49 }
50}
51
52#[derive(thiserror::Error, Debug)]
54pub enum SocketAddressParsingError {
55 #[error("could not parse socket address: {0}")]
57 Io(#[from] std::io::Error),
58 #[error("cannot parse socket address from empty string")]
60 Empty,
61 #[error("could not parse socket address from {0}")]
63 Parse(String),
64 #[error("could not parse port: {0}")]
66 Port(#[from] std::num::ParseIntError),
67}
68
69pub fn parse_socket_address(value: &str) -> eyre::Result<SocketAddr, SocketAddressParsingError> {
80 if value.is_empty() {
81 return Err(SocketAddressParsingError::Empty)
82 }
83
84 if let Some(port) = value.strip_prefix(':').or_else(|| value.strip_prefix("localhost:")) {
85 let port: u16 = port.parse()?;
86 return Ok(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port))
87 }
88 if let Ok(port) = value.parse() {
89 return Ok(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port))
90 }
91 value
92 .to_socket_addrs()?
93 .next()
94 .ok_or_else(|| SocketAddressParsingError::Parse(value.to_string()))
95}
96
97pub fn read_json_from_file<T: serde::de::DeserializeOwned>(path: &str) -> Result<T, FsPathError> {
99 reth_fs_util::read_json_file(Path::new(path))
100}
101
102pub fn parse_ether_value(value: &str) -> eyre::Result<u128> {
111 let eth = value.parse::<f64>()?;
112 if eth.is_sign_negative() {
113 return Err(eyre::eyre!("Ether value cannot be negative"))
114 }
115 let wei = eth * 1e18;
116 Ok(wei as u128)
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use rand::Rng;
123
124 #[test]
125 fn parse_socket_addresses() {
126 for value in ["localhost:9000", ":9000", "9000"] {
127 let socket_addr = parse_socket_address(value)
128 .unwrap_or_else(|_| panic!("could not parse socket address: {value}"));
129
130 assert!(socket_addr.ip().is_loopback());
131 assert_eq!(socket_addr.port(), 9000);
132 }
133 }
134
135 #[test]
136 fn parse_socket_address_random() {
137 let port: u16 = rand::rng().random();
138
139 for value in [format!("localhost:{port}"), format!(":{port}"), port.to_string()] {
140 let socket_addr = parse_socket_address(&value)
141 .unwrap_or_else(|_| panic!("could not parse socket address: {value}"));
142
143 assert!(socket_addr.ip().is_loopback());
144 assert_eq!(socket_addr.port(), port);
145 }
146 }
147
148 #[test]
149 fn parse_ms_or_seconds() {
150 let ms = parse_duration_from_secs_or_ms("5ms").unwrap();
151 assert_eq!(ms, Duration::from_millis(5));
152
153 let seconds = parse_duration_from_secs_or_ms("5").unwrap();
154 assert_eq!(seconds, Duration::from_secs(5));
155
156 let seconds = parse_duration_from_secs_or_ms("5s").unwrap();
157 assert_eq!(seconds, Duration::from_secs(5));
158
159 assert!(parse_duration_from_secs_or_ms("5ns").is_err());
160 }
161
162 #[test]
163 fn parse_ether_values() {
164 let wei = parse_ether_value("1.05").unwrap();
166 assert_eq!(wei, 1_050_000_000_000_000_000u128);
167
168 let wei = parse_ether_value("2").unwrap();
170 assert_eq!(wei, 2_000_000_000_000_000_000u128);
171
172 let wei = parse_ether_value("0").unwrap();
174 assert_eq!(wei, 0);
175
176 assert!(parse_ether_value("-1").is_err());
178
179 assert!(parse_ether_value("abc").is_err());
181 }
182}