-
Notifications
You must be signed in to change notification settings - Fork 275
/
Copy pathutils.rs
167 lines (145 loc) · 5.68 KB
/
utils.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! Utility functions for the node bindings.
use std::{
borrow::Cow,
future::Future,
net::{SocketAddr, TcpListener},
path::PathBuf,
};
use tempfile::TempDir;
/// A bit of hack to find an unused TCP port.
///
/// Does not guarantee that the given port is unused after the function exists, just that it was
/// unused before the function started (i.e., it does not reserve a port).
pub(crate) fn unused_port() -> u16 {
let listener = TcpListener::bind("127.0.0.1:0")
.expect("Failed to create TCP listener to find unused port");
let local_addr =
listener.local_addr().expect("Failed to read TCP listener local_addr to find unused port");
local_addr.port()
}
/// Extracts the value for the given key from the line of text.
///
/// It supports keys that end with '=' or ': '.
/// For keys end with '=', find value until ' ' is encountered or end of line
/// For keys end with ':', find value until ',' is encountered or end of line
pub(crate) fn extract_value<'a>(key: &str, line: &'a str) -> Option<&'a str> {
let mut key_equal = Cow::from(key);
let mut key_colon = Cow::from(key);
// Prepare both key variants
if !key_equal.ends_with('=') {
key_equal = format!("{}=", key).into();
}
if !key_colon.ends_with(": ") {
key_colon = format!("{}: ", key).into();
}
// Try to find the key with '='
if let Some(pos) = line.find(key_equal.as_ref()) {
let start = pos + key_equal.len();
let end = line[start..].find(' ').map(|i| start + i).unwrap_or(line.len());
if start <= line.len() && end <= line.len() {
return Some(line[start..end].trim());
}
}
// If not found, try to find the key with ': '
if let Some(pos) = line.find(key_colon.as_ref()) {
let start = pos + key_colon.len();
let end = line[start..].find(',').map(|i| start + i).unwrap_or(line.len()); // Assuming comma or end of line
if start <= line.len() && end <= line.len() {
return Some(line[start..end].trim());
}
}
// If neither variant matches, return None
None
}
/// Extracts the endpoint from the given line.
pub(crate) fn extract_endpoint(key: &str, line: &str) -> Option<SocketAddr> {
extract_value(key, line)
.map(|val| val.trim_start_matches("Some(").trim_end_matches(')'))
.and_then(|val| val.parse().ok())
}
/// Runs the given closure with a temporary directory.
pub fn run_with_tempdir_sync(prefix: &str, f: impl FnOnce(PathBuf)) {
let temp_dir = TempDir::with_prefix(prefix).unwrap();
let temp_dir_path = temp_dir.path().to_path_buf();
f(temp_dir_path);
#[cfg(not(windows))]
temp_dir.close().unwrap();
}
/// Runs the given async closure with a temporary directory.
pub async fn run_with_tempdir<F, Fut>(prefix: &str, f: F)
where
F: FnOnce(PathBuf) -> Fut,
Fut: Future<Output = ()>,
{
let temp_dir = TempDir::with_prefix(prefix).unwrap();
let temp_dir_path = temp_dir.path().to_path_buf();
f(temp_dir_path).await;
#[cfg(not(windows))]
temp_dir.close().unwrap();
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::SocketAddr;
#[test]
fn test_extract_value_with_equals() {
let line = "key=value some other text";
assert_eq!(extract_value("key", line), Some("value"));
}
#[test]
fn test_extract_value_with_colon() {
let line = "key: value, more text here";
assert_eq!(extract_value("key", line), Some("value"));
}
#[test]
fn test_extract_value_not_found() {
let line = "unrelated text";
assert_eq!(extract_value("key", line), None);
}
#[test]
fn test_extract_value_equals_no_space() {
let line = "INFO key=";
assert_eq!(extract_value("key", line), Some(""))
}
#[test]
fn test_extract_value_colon_no_comma() {
let line = "INFO key: value";
assert_eq!(extract_value("key", line), Some("value"))
}
#[test]
fn test_extract_http_address() {
let line = "INFO [07-01|13:20:42.774] HTTP server started endpoint=127.0.0.1:8545 auth=false prefix= cors= vhosts=localhost";
assert_eq!(
extract_endpoint("endpoint=", line),
Some(SocketAddr::from(([127, 0, 0, 1], 8545)))
);
}
#[test]
fn test_extract_udp_address() {
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\")], .. }";
assert_eq!(
extract_endpoint("IpV4 TCP Socket: ", line),
Some(SocketAddr::from(([0, 0, 0, 0], 30303)))
);
}
#[test]
fn test_unused_port() {
let port = unused_port();
assert!(port > 0);
}
#[test]
fn test_run_with_tempdir_sync() {
run_with_tempdir_sync("test_prefix", |path| {
assert!(path.exists(), "Temporary directory should exist");
assert!(path.is_dir(), "Temporary directory should be a directory");
});
}
#[tokio::test]
async fn test_run_with_tempdir_async() {
run_with_tempdir("test_prefix", |path| async move {
assert!(path.exists(), "Temporary directory should exist");
assert!(path.is_dir(), "Temporary directory should be a directory");
})
.await;
}
}