Skip to main content

reth_bench/bench/
helpers.rs

1//! Common helpers for reth-bench commands.
2
3use eyre::Result;
4use std::{
5    io::{BufReader, Read},
6    time::Duration,
7};
8
9/// Read input from either a file path or stdin.
10pub(crate) fn read_input(path: Option<&str>) -> Result<String> {
11    Ok(match path {
12        Some(path) => reth_fs_util::read_to_string(path)?,
13        None => String::from_utf8(
14            BufReader::new(std::io::stdin()).bytes().collect::<Result<Vec<_>, _>>()?,
15        )?,
16    })
17}
18
19/// Load JWT secret from either a file or use the provided string directly.
20pub(crate) fn load_jwt_secret(jwt_secret: Option<&str>) -> Result<Option<String>> {
21    match jwt_secret {
22        Some(secret) => {
23            // Try to read as file first
24            match std::fs::read_to_string(secret) {
25                Ok(contents) => Ok(Some(contents.trim().to_string())),
26                // If file read fails, use the string directly
27                Err(_) => Ok(Some(secret.to_string())),
28            }
29        }
30        None => Ok(None),
31    }
32}
33
34/// Parses a gas limit value with optional suffix: K for thousand, M for million, G for billion.
35///
36/// Examples: "30000000", "30M", "1G", "2G"
37pub(crate) fn parse_gas_limit(s: &str) -> eyre::Result<u64> {
38    let s = s.trim();
39    if s.is_empty() {
40        return Err(eyre::eyre!("empty value"));
41    }
42
43    let (num_str, multiplier) = if let Some(prefix) = s.strip_suffix(['G', 'g']) {
44        (prefix, 1_000_000_000u64)
45    } else if let Some(prefix) = s.strip_suffix(['M', 'm']) {
46        (prefix, 1_000_000u64)
47    } else if let Some(prefix) = s.strip_suffix(['K', 'k']) {
48        (prefix, 1_000u64)
49    } else {
50        (s, 1u64)
51    };
52
53    let base: u64 = num_str.trim().parse()?;
54    base.checked_mul(multiplier).ok_or_else(|| eyre::eyre!("value overflow"))
55}
56
57/// Parses a duration string, treating bare integers as milliseconds.
58///
59/// Accepts either a `humantime` duration string (e.g. `"100ms"`, `"2s"`) or a plain
60/// integer which is interpreted as milliseconds (e.g. `"400"` → 400ms).
61pub(crate) fn parse_duration(s: &str) -> eyre::Result<Duration> {
62    match humantime::parse_duration(s) {
63        Ok(d) => Ok(d),
64        Err(_) => {
65            let millis: u64 =
66                s.trim().parse().map_err(|_| eyre::eyre!("invalid duration: {s:?}"))?;
67            Ok(Duration::from_millis(millis))
68        }
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_parse_gas_limit_plain_number() {
78        assert_eq!(parse_gas_limit("30000000").unwrap(), 30_000_000);
79        assert_eq!(parse_gas_limit("1").unwrap(), 1);
80        assert_eq!(parse_gas_limit("0").unwrap(), 0);
81    }
82
83    #[test]
84    fn test_parse_gas_limit_k_suffix() {
85        assert_eq!(parse_gas_limit("1K").unwrap(), 1_000);
86        assert_eq!(parse_gas_limit("30k").unwrap(), 30_000);
87        assert_eq!(parse_gas_limit("100K").unwrap(), 100_000);
88    }
89
90    #[test]
91    fn test_parse_gas_limit_m_suffix() {
92        assert_eq!(parse_gas_limit("1M").unwrap(), 1_000_000);
93        assert_eq!(parse_gas_limit("30m").unwrap(), 30_000_000);
94        assert_eq!(parse_gas_limit("100M").unwrap(), 100_000_000);
95    }
96
97    #[test]
98    fn test_parse_gas_limit_g_suffix() {
99        assert_eq!(parse_gas_limit("1G").unwrap(), 1_000_000_000);
100        assert_eq!(parse_gas_limit("2g").unwrap(), 2_000_000_000);
101        assert_eq!(parse_gas_limit("10G").unwrap(), 10_000_000_000);
102    }
103
104    #[test]
105    fn test_parse_gas_limit_with_whitespace() {
106        assert_eq!(parse_gas_limit(" 1G ").unwrap(), 1_000_000_000);
107        assert_eq!(parse_gas_limit("2 M").unwrap(), 2_000_000);
108    }
109
110    #[test]
111    fn test_parse_gas_limit_errors() {
112        assert!(parse_gas_limit("").is_err());
113        assert!(parse_gas_limit("abc").is_err());
114        assert!(parse_gas_limit("G").is_err());
115        assert!(parse_gas_limit("-1G").is_err());
116    }
117
118    #[test]
119    fn test_parse_duration_with_unit() {
120        assert_eq!(parse_duration("100ms").unwrap(), Duration::from_millis(100));
121        assert_eq!(parse_duration("2s").unwrap(), Duration::from_secs(2));
122        assert_eq!(parse_duration("1m").unwrap(), Duration::from_secs(60));
123    }
124
125    #[test]
126    fn test_parse_duration_bare_millis() {
127        assert_eq!(parse_duration("400").unwrap(), Duration::from_millis(400));
128        assert_eq!(parse_duration("0").unwrap(), Duration::from_millis(0));
129        assert_eq!(parse_duration("1000").unwrap(), Duration::from_millis(1000));
130    }
131
132    #[test]
133    fn test_parse_duration_errors() {
134        assert!(parse_duration("abc").is_err());
135        assert!(parse_duration("").is_err());
136    }
137}