alloy_node_bindings/
utils.rs1use std::{
4 borrow::Cow,
5 future::Future,
6 net::{SocketAddr, TcpListener},
7 path::PathBuf,
8};
9use tempfile::TempDir;
10
11pub(crate) fn unused_port() -> u16 {
16 let listener = TcpListener::bind("127.0.0.1:0")
17 .expect("Failed to create TCP listener to find unused port");
18
19 let local_addr =
20 listener.local_addr().expect("Failed to read TCP listener local_addr to find unused port");
21 local_addr.port()
22}
23
24pub(crate) fn extract_value<'a>(key: &str, line: &'a str) -> Option<&'a str> {
30 let mut key_equal = Cow::from(key);
31 let mut key_colon = Cow::from(key);
32
33 if !key_equal.ends_with('=') {
35 key_equal = format!("{key}=").into();
36 }
37 if !key_colon.ends_with(": ") {
38 key_colon = format!("{key}: ").into();
39 }
40
41 if let Some(pos) = line.find(key_equal.as_ref()) {
43 let start = pos + key_equal.len();
44 let end = line[start..].find(' ').map(|i| start + i).unwrap_or(line.len());
45 if start <= line.len() && end <= line.len() {
46 return Some(line[start..end].trim());
47 }
48 }
49
50 if let Some(pos) = line.find(key_colon.as_ref()) {
52 let start = pos + key_colon.len();
53 let end = line[start..].find(',').map(|i| start + i).unwrap_or(line.len()); if start <= line.len() && end <= line.len() {
55 return Some(line[start..end].trim());
56 }
57 }
58
59 None
61}
62
63pub(crate) fn extract_endpoint(key: &str, line: &str) -> Option<SocketAddr> {
65 extract_value(key, line)
66 .map(|val| val.trim_start_matches("Some(").trim_end_matches(')'))
67 .and_then(|val| val.parse().ok())
68}
69
70pub fn run_with_tempdir_sync(prefix: &str, f: impl FnOnce(PathBuf)) {
72 let temp_dir = TempDir::with_prefix(prefix).unwrap();
73 let temp_dir_path = temp_dir.path().to_path_buf();
74 f(temp_dir_path);
75 #[cfg(not(windows))]
76 temp_dir.close().unwrap();
77}
78
79pub async fn run_with_tempdir<F, Fut>(prefix: &str, f: F)
81where
82 F: FnOnce(PathBuf) -> Fut,
83 Fut: Future<Output = ()>,
84{
85 let temp_dir = TempDir::with_prefix(prefix).unwrap();
86 let temp_dir_path = temp_dir.path().to_path_buf();
87 f(temp_dir_path).await;
88 #[cfg(not(windows))]
89 temp_dir.close().unwrap();
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use std::net::SocketAddr;
96
97 #[test]
98 fn test_extract_value_with_equals() {
99 let line = "key=value some other text";
100 assert_eq!(extract_value("key", line), Some("value"));
101 }
102
103 #[test]
104 fn test_extract_value_with_colon() {
105 let line = "key: value, more text here";
106 assert_eq!(extract_value("key", line), Some("value"));
107 }
108
109 #[test]
110 fn test_extract_value_not_found() {
111 let line = "unrelated text";
112 assert_eq!(extract_value("key", line), None);
113 }
114
115 #[test]
116 fn test_extract_value_equals_no_space() {
117 let line = "INFO key=";
118 assert_eq!(extract_value("key", line), Some(""))
119 }
120
121 #[test]
122 fn test_extract_value_colon_no_comma() {
123 let line = "INFO key: value";
124 assert_eq!(extract_value("key", line), Some("value"))
125 }
126
127 #[test]
128 fn test_extract_http_address() {
129 let line = "INFO [07-01|13:20:42.774] HTTP server started endpoint=127.0.0.1:8545 auth=false prefix= cors= vhosts=localhost";
130 assert_eq!(
131 extract_endpoint("endpoint=", line),
132 Some(SocketAddr::from(([127, 0, 0, 1], 8545)))
133 );
134 }
135
136 #[test]
137 fn test_extract_udp_address() {
138 let line = "Updated local ENR enr=Enr { id: Some(\"v4\"), seq: 2, NodeId: 0x04dad428038b4db230fc5298646e137564fc6861662f32bdbf220f31299bdde7, signature: \"416520d69bfd701d95f4b77778970a5c18fa86e4dd4dc0746e80779d986c68605f491c01ef39cd3739fdefc1e3558995ad2f5d325f9e1db795896799e8ee94a3\", IpV4 UDP Socket: Some(0.0.0.0:30303), IpV6 UDP Socket: None, IpV4 TCP Socket: Some(0.0.0.0:30303), IpV6 TCP Socket: None, Other Pairs: [(\"eth\", \"c984fc64ec0483118c30\"), (\"secp256k1\", \"a103aa181e8fd5df651716430f1d4b504b54d353b880256f56aa727beadd1b7a9766\")], .. }";
139 assert_eq!(
140 extract_endpoint("IpV4 TCP Socket: ", line),
141 Some(SocketAddr::from(([0, 0, 0, 0], 30303)))
142 );
143 }
144
145 #[test]
146 fn test_unused_port() {
147 let port = unused_port();
148 assert!(port > 0);
149 }
150
151 #[test]
152 fn test_run_with_tempdir_sync() {
153 run_with_tempdir_sync("test_prefix", |path| {
154 assert!(path.exists(), "Temporary directory should exist");
155 assert!(path.is_dir(), "Temporary directory should be a directory");
156 });
157 }
158
159 #[tokio::test]
160 async fn test_run_with_tempdir_async() {
161 run_with_tempdir("test_prefix", |path| async move {
162 assert!(path.exists(), "Temporary directory should exist");
163 assert!(path.is_dir(), "Temporary directory should be a directory");
164 })
165 .await;
166 }
167}