mirror of
https://github.com/epi052/feroxbuster.git
synced 2026-06-08 02:31:16 -03:00
fix clippy errors when denying warnings
This commit is contained in:
@@ -73,9 +73,7 @@ where
|
||||
|
||||
let identity = reqwest::Identity::from_pkcs8_pem(&cert, &key).with_context(|| {
|
||||
format!(
|
||||
"either {} or {} are invalid; expecting PEM encoded certificate and key",
|
||||
cert_path, key_path
|
||||
)
|
||||
"either {cert_path} or {key_path} are invalid; expecting PEM encoded certificate and key")
|
||||
})?;
|
||||
|
||||
client = client.identity(identity);
|
||||
|
||||
@@ -1020,7 +1020,7 @@ impl Configuration {
|
||||
// Split into name and value at the first equals sign
|
||||
let name = &trimmed[..pos].trim();
|
||||
let value = &trimmed[pos + 1..].trim();
|
||||
Some(format!("{}={}", name, value))
|
||||
Some(format!("{name}={value}"))
|
||||
} else {
|
||||
// Handle the case where there's no equals sign
|
||||
Some(trimmed.to_string())
|
||||
|
||||
@@ -359,7 +359,7 @@ fn combine_cookies(cookie1: &str, cookie2: &str) -> String {
|
||||
// Build the final cookie header string
|
||||
cookie_map
|
||||
.into_iter()
|
||||
.map(|(key, value)| format!("{}={}", key, value))
|
||||
.map(|(key, value)| format!("{key}={value}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("; ")
|
||||
}
|
||||
@@ -522,12 +522,32 @@ pub fn parse_request_file(config: &mut Configuration) -> Result<()> {
|
||||
|
||||
let url = parse_url_with_raw_path(uri);
|
||||
|
||||
if url.is_err() {
|
||||
if let Ok(mut url) = url {
|
||||
if let Some(host) = config.headers.get("Host") {
|
||||
url.set_host(Some(host)).unwrap();
|
||||
}
|
||||
|
||||
url.query_pairs().for_each(|(key, value)| {
|
||||
for (k, _) in &config.queries {
|
||||
if k.to_lowercase() == key.to_lowercase() {
|
||||
// allow cli options to take precedent when query names match
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
config.queries.push((key.to_string(), value.to_string()));
|
||||
});
|
||||
|
||||
url.set_query(None);
|
||||
url.set_fragment(None);
|
||||
|
||||
config.target_url = url.to_string();
|
||||
} else {
|
||||
// uri in request line is not a valid URL, so it's most likely a path/relative url
|
||||
// we need to combine it with the host header
|
||||
for (key, value) in &config.headers {
|
||||
if key.to_lowercase() == "host" {
|
||||
config.target_url = format!("{}{}", value, uri);
|
||||
config.target_url = format!("{value}{uri}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -559,28 +579,6 @@ pub fn parse_request_file(config: &mut Configuration) -> Result<()> {
|
||||
config.queries.push((name, value));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let mut url = url.unwrap();
|
||||
|
||||
if let Some(host) = config.headers.get("Host") {
|
||||
url.set_host(Some(host)).unwrap();
|
||||
}
|
||||
|
||||
url.query_pairs().for_each(|(key, value)| {
|
||||
for (k, _) in &config.queries {
|
||||
if k.to_lowercase() == key.to_lowercase() {
|
||||
// allow cli options to take precedent when query names match
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
config.queries.push((key.to_string(), value.to_string()));
|
||||
});
|
||||
|
||||
url.set_query(None);
|
||||
url.set_fragment(None);
|
||||
|
||||
config.target_url = url.to_string();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -112,7 +112,7 @@ impl Handles {
|
||||
pub fn set_scan_handle(&self, handle: ScanHandle) {
|
||||
if let Ok(mut guard) = self.scans.write() {
|
||||
if guard.is_none() {
|
||||
let _ = std::mem::replace(&mut *guard, Some(handle));
|
||||
guard.replace(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ impl TermInputHandler {
|
||||
|
||||
let Ok(mut state_file) = open_file(&temp_filename.to_string_lossy()) else {
|
||||
// couldn't open the fallback file, let the user know
|
||||
let error = format!("❌❌ Could not save {:?}, giving up...", temp_filename);
|
||||
let error = format!("❌❌ Could not save {temp_filename:?}, giving up...");
|
||||
PROGRESS_PRINTER.println(error);
|
||||
|
||||
log::trace!("exit: sigint_handler (failed to write)");
|
||||
@@ -126,7 +126,7 @@ impl TermInputHandler {
|
||||
|
||||
write_to(&state, &mut state_file, true)?;
|
||||
|
||||
let msg = format!("✅ Saved scan state to {:?}", temp_filename);
|
||||
let msg = format!("✅ Saved scan state to {temp_filename:?}");
|
||||
PROGRESS_PRINTER.println(msg);
|
||||
|
||||
log::trace!("exit: sigint_handler (saved to temp folder)");
|
||||
|
||||
@@ -402,7 +402,7 @@ impl TermOutHandler {
|
||||
let url = response.url();
|
||||
|
||||
// confirmed safe: see src/response.rs for comments
|
||||
let filename = url.path_segments().unwrap().last().unwrap();
|
||||
let filename = url.path_segments().unwrap().next_back().unwrap();
|
||||
|
||||
if !filename.is_empty() {
|
||||
// append rules
|
||||
@@ -501,7 +501,7 @@ mod tests {
|
||||
|
||||
let paths: Vec<_> = urls
|
||||
.iter()
|
||||
.map(|url| url.path_segments().unwrap().last().unwrap())
|
||||
.map(|url| url.path_segments().unwrap().next_back().unwrap())
|
||||
.collect();
|
||||
|
||||
assert_eq!(urls.len(), 7);
|
||||
@@ -545,7 +545,7 @@ mod tests {
|
||||
|
||||
let paths: Vec<_> = urls
|
||||
.iter()
|
||||
.map(|url| url.path_segments().unwrap().last().unwrap())
|
||||
.map(|url| url.path_segments().unwrap().next_back().unwrap())
|
||||
.collect();
|
||||
|
||||
assert_eq!(urls.len(), 6);
|
||||
|
||||
@@ -110,7 +110,7 @@ impl ScanHandler {
|
||||
fn wordlist(&self, wordlist: Arc<Vec<String>>) {
|
||||
if let Ok(mut guard) = self.wordlist.lock() {
|
||||
if guard.is_none() {
|
||||
let _ = std::mem::replace(&mut *guard, Some(wordlist));
|
||||
guard.replace(wordlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,12 +209,12 @@ impl ScanHandler {
|
||||
///
|
||||
/// updating all bar lengths correctly requires a few different actions on our part.
|
||||
/// - get the current number of requests expected per scan (dynamic when --collect-extensions
|
||||
/// is used)
|
||||
/// is used)
|
||||
/// - update the overall progress bar via the statistics handler (total expected)
|
||||
/// - update the expected per scan value tracked in the statistics handler
|
||||
/// - update progress bars on each FeroxScan (type::directory) that are running/not-started
|
||||
/// - update progress bar length on FeroxScans (this is used when creating new a FeroxScan and
|
||||
/// determines the new scan's progress bar length)
|
||||
/// determines the new scan's progress bar length)
|
||||
fn update_all_bar_lengths(&self) -> Result<()> {
|
||||
log::trace!("enter: update_all_bar_lengths");
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ impl FeroxFilter for EmptyFilter {
|
||||
|
||||
/// Compare one EmptyFilter to another
|
||||
fn box_eq(&self, other: &dyn Any) -> bool {
|
||||
other.downcast_ref::<Self>().map_or(false, |a| self == a)
|
||||
other.downcast_ref::<Self>() == Some(self)
|
||||
}
|
||||
|
||||
/// Return self as Any for dynamic dispatch purposes
|
||||
|
||||
@@ -23,7 +23,7 @@ impl FeroxFilter for LinesFilter {
|
||||
|
||||
/// Compare one LinesFilter to another
|
||||
fn box_eq(&self, other: &dyn Any) -> bool {
|
||||
other.downcast_ref::<Self>().map_or(false, |a| self == a)
|
||||
other.downcast_ref::<Self>() == Some(self)
|
||||
}
|
||||
|
||||
/// Return self as Any for dynamic dispatch purposes
|
||||
|
||||
@@ -41,7 +41,7 @@ impl FeroxFilter for RegexFilter {
|
||||
|
||||
/// Compare one SizeFilter to another
|
||||
fn box_eq(&self, other: &dyn Any) -> bool {
|
||||
other.downcast_ref::<Self>().map_or(false, |a| self == a)
|
||||
other.downcast_ref::<Self>() == Some(self)
|
||||
}
|
||||
|
||||
/// Return self as Any for dynamic dispatch purposes
|
||||
|
||||
@@ -39,7 +39,7 @@ impl FeroxFilter for SimilarityFilter {
|
||||
fn box_eq(&self, other: &dyn Any) -> bool {
|
||||
other
|
||||
.downcast_ref::<Self>()
|
||||
.map_or(false, |a| self.hash == a.hash)
|
||||
.is_some_and(|a| self.hash == a.hash)
|
||||
}
|
||||
|
||||
/// Return self as Any for dynamic dispatch purposes
|
||||
|
||||
@@ -23,7 +23,7 @@ impl FeroxFilter for SizeFilter {
|
||||
|
||||
/// Compare one SizeFilter to another
|
||||
fn box_eq(&self, other: &dyn Any) -> bool {
|
||||
other.downcast_ref::<Self>().map_or(false, |a| self == a)
|
||||
other.downcast_ref::<Self>() == Some(self)
|
||||
}
|
||||
|
||||
/// Return self as Any for dynamic dispatch purposes
|
||||
|
||||
@@ -30,7 +30,7 @@ impl FeroxFilter for StatusCodeFilter {
|
||||
|
||||
/// Compare one StatusCodeFilter to another
|
||||
fn box_eq(&self, other: &dyn Any) -> bool {
|
||||
other.downcast_ref::<Self>().map_or(false, |a| self == a)
|
||||
other.downcast_ref::<Self>() == Some(self)
|
||||
}
|
||||
|
||||
/// Return self as Any for dynamic dispatch purposes
|
||||
|
||||
@@ -144,7 +144,7 @@ impl FeroxFilter for WildcardFilter {
|
||||
|
||||
/// Compare one WildcardFilter to another
|
||||
fn box_eq(&self, other: &dyn Any) -> bool {
|
||||
other.downcast_ref::<Self>().map_or(false, |a| self == a)
|
||||
other.downcast_ref::<Self>() == Some(self)
|
||||
}
|
||||
|
||||
/// Return self as Any for dynamic dispatch purposes
|
||||
@@ -175,6 +175,6 @@ impl std::fmt::Display for WildcardFilter {
|
||||
),
|
||||
OutputLevel::Default,
|
||||
);
|
||||
write!(f, "{}", msg)
|
||||
write!(f, "{msg}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ impl FeroxFilter for WordsFilter {
|
||||
|
||||
/// Compare one WordsFilter to another
|
||||
fn box_eq(&self, other: &dyn Any) -> bool {
|
||||
other.downcast_ref::<Self>().map_or(false, |a| self == a)
|
||||
other.downcast_ref::<Self>() == Some(self)
|
||||
}
|
||||
|
||||
/// Return self as Any for dynamic dispatch purposes
|
||||
|
||||
@@ -287,7 +287,7 @@ impl HeuristicTests {
|
||||
// and then we want to add any extensions that was specified
|
||||
// or has since been added to the running config
|
||||
for ext in &self.handles.config.extensions {
|
||||
extensions.push(format!(".{}", ext));
|
||||
extensions.push(format!(".{ext}"));
|
||||
}
|
||||
|
||||
// for every method, attempt to id its 404 response
|
||||
@@ -409,7 +409,7 @@ impl HeuristicTests {
|
||||
|
||||
// if we're here, we've found a new wildcard that we didn't previously display, print it
|
||||
if print_sentry {
|
||||
ferox_print(&format!("{}", new_wildcard), &PROGRESS_PRINTER);
|
||||
ferox_print(&format!("{new_wildcard}"), &PROGRESS_PRINTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
src/main.rs
15
src/main.rs
@@ -226,12 +226,12 @@ async fn wrapped_main(config: Arc<Configuration>) -> Result<()> {
|
||||
// check if update_app is true
|
||||
if config.update_app {
|
||||
match update_app().await {
|
||||
Err(e) => eprintln!("\n[ERROR] {}", e),
|
||||
Err(e) => eprintln!("\n[ERROR] {e}"),
|
||||
Ok(self_update::Status::UpToDate(version)) => {
|
||||
eprintln!("\nFeroxbuster {} is up to date", version)
|
||||
eprintln!("\nFeroxbuster {version} is up to date")
|
||||
}
|
||||
Ok(self_update::Status::Updated(version)) => {
|
||||
eprintln!("\nFeroxbuster updated to {} version", version)
|
||||
eprintln!("\nFeroxbuster updated to {version} version")
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
@@ -259,11 +259,11 @@ async fn wrapped_main(config: Arc<Configuration>) -> Result<()> {
|
||||
}
|
||||
|
||||
// attempt to get the filename from the url's path
|
||||
let Some(path_segments) = response.url().path_segments() else {
|
||||
let Some(mut path_segments) = response.url().path_segments() else {
|
||||
bail!("Unable to parse path from url: {}", response.url());
|
||||
};
|
||||
|
||||
let Some(filename) = path_segments.last() else {
|
||||
let Some(filename) = path_segments.next_back() else {
|
||||
bail!(
|
||||
"Unable to parse filename from url's path: {}",
|
||||
response.url().path()
|
||||
@@ -477,13 +477,14 @@ async fn wrapped_main(config: Arc<Configuration>) -> Result<()> {
|
||||
if n > 0 {
|
||||
let trimmed = buf.trim();
|
||||
if !trimmed.is_empty() {
|
||||
println!("{}", trimmed);
|
||||
println!("{trimmed}");
|
||||
}
|
||||
buf.clear();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let _ = output.wait();
|
||||
drop(permit);
|
||||
});
|
||||
}
|
||||
@@ -612,7 +613,7 @@ async fn clean_up(handles: Arc<Handles>, tasks: Tasks) -> Result<()> {
|
||||
}
|
||||
|
||||
async fn update_app() -> Result<self_update::Status, Box<dyn ::std::error::Error>> {
|
||||
let target_os = format!("{}-{}", ARCH, OS);
|
||||
let target_os = format!("{ARCH}-{OS}");
|
||||
let status = tokio::task::spawn_blocking(move || {
|
||||
self_update::backends::github::Update::configure()
|
||||
.repo_owner("epi052")
|
||||
|
||||
@@ -190,15 +190,14 @@ impl FeroxResponse {
|
||||
///
|
||||
/// Additionally, inspects query parameters, as they're also often indicative of a file
|
||||
pub fn is_file(&self) -> bool {
|
||||
let has_extension = match self.url.path_segments() {
|
||||
Some(path) => {
|
||||
if let Some(last) = path.last() {
|
||||
last.contains('.') // last segment has some sort of extension, probably
|
||||
} else {
|
||||
false
|
||||
}
|
||||
let has_extension = if let Some(mut path) = self.url.path_segments() {
|
||||
if let Some(last) = path.next_back() {
|
||||
last.contains('.') // last segment has some sort of extension, probably
|
||||
} else {
|
||||
false
|
||||
}
|
||||
None => false,
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
self.url.query_pairs().count() > 0 || has_extension
|
||||
@@ -279,7 +278,7 @@ impl FeroxResponse {
|
||||
// (which may be empty).
|
||||
//
|
||||
// meaning: the two unwraps here are fine, the worst outcome is an empty string
|
||||
let filename = self.url.path_segments().unwrap().last().unwrap();
|
||||
let filename = self.url.path_segments().unwrap().next_back().unwrap();
|
||||
|
||||
if !filename.is_empty() {
|
||||
// non-empty string, try to get extension
|
||||
|
||||
@@ -198,7 +198,7 @@ impl FeroxScan {
|
||||
/// small wrapper to set the JoinHandle
|
||||
pub async fn set_task(&self, task: JoinHandle<()>) -> Result<()> {
|
||||
let mut guard = self.task.lock().await;
|
||||
let _ = std::mem::replace(&mut *guard, Some(task));
|
||||
guard.replace(task);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ impl FeroxScan {
|
||||
|
||||
pb.set_position(self.requests_made_so_far);
|
||||
|
||||
let _ = std::mem::replace(&mut *guard, Some(pb.clone()));
|
||||
guard.replace(pb.clone());
|
||||
|
||||
pb
|
||||
}
|
||||
|
||||
@@ -474,7 +474,10 @@ impl FeroxScans {
|
||||
|
||||
self.menu.clear_screen();
|
||||
|
||||
let banner = Banner::new(&[handles.config.target_url.clone()], &handles.config);
|
||||
let banner = Banner::new(
|
||||
std::slice::from_ref(&handles.config.target_url),
|
||||
&handles.config,
|
||||
);
|
||||
banner
|
||||
.print_to(&self.menu.term, handles.config.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -703,7 +703,7 @@ fn menu_get_command_input_from_user_returns_cancel() {
|
||||
let menu = Menu::new();
|
||||
|
||||
for (idx, cmd) in ["cancel", "Cancel", "c", "C"].iter().enumerate() {
|
||||
let force = idx % 2 == 0;
|
||||
let force = idx.is_multiple_of(2);
|
||||
|
||||
let full_cmd = if force {
|
||||
format!("{cmd} -f {idx}\n")
|
||||
|
||||
@@ -480,8 +480,9 @@ impl Requester {
|
||||
if let Ok(mut guard) = TF_IDF.write() {
|
||||
if let Some(doc) = Document::from_html(ferox_response.text()) {
|
||||
guard.add_document(doc);
|
||||
if guard.num_documents() % 12 == 0
|
||||
|| (guard.num_documents() < 5 && guard.num_documents() % 2 == 0)
|
||||
if guard.num_documents().is_multiple_of(12)
|
||||
|| (guard.num_documents() < 5
|
||||
&& guard.num_documents().is_multiple_of(2))
|
||||
{
|
||||
guard.calculate_tf_idf_scores();
|
||||
}
|
||||
|
||||
@@ -50,32 +50,31 @@ impl Display for dyn FeroxFilter {
|
||||
unreachable!("wildcard filter without any filters set");
|
||||
}
|
||||
(None, None, Some(lc)) => {
|
||||
msg.push_str(&format!("containing {} lines", lc));
|
||||
msg.push_str(&format!("containing {lc} lines"));
|
||||
}
|
||||
(None, Some(wc), None) => {
|
||||
msg.push_str(&format!("containing {} words", wc));
|
||||
msg.push_str(&format!("containing {wc} words"));
|
||||
}
|
||||
(None, Some(wc), Some(lc)) => {
|
||||
msg.push_str(&format!("containing {} words and {} lines", wc, lc));
|
||||
msg.push_str(&format!("containing {wc} words and {lc} lines"));
|
||||
}
|
||||
(Some(cl), None, None) => {
|
||||
msg.push_str(&format!("containing {} bytes", cl));
|
||||
msg.push_str(&format!("containing {cl} bytes"));
|
||||
}
|
||||
(Some(cl), None, Some(lc)) => {
|
||||
msg.push_str(&format!("containing {} bytes and {} lines", cl, lc));
|
||||
msg.push_str(&format!("containing {cl} bytes and {lc} lines"));
|
||||
}
|
||||
(Some(cl), Some(wc), None) => {
|
||||
msg.push_str(&format!("containing {} bytes and {} words", cl, wc));
|
||||
msg.push_str(&format!("containing {cl} bytes and {wc} words"));
|
||||
}
|
||||
(Some(cl), Some(wc), Some(lc)) => {
|
||||
msg.push_str(&format!(
|
||||
"containing {} bytes, {} words, and {} lines",
|
||||
cl, wc, lc
|
||||
"containing {cl} bytes, {wc} words, and {lc} lines"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, "{}", msg)
|
||||
write!(f, "{msg}")
|
||||
} else if let Some(filter) = self.as_any().downcast_ref::<StatusCodeFilter>() {
|
||||
write!(f, "Status code: {}", style(filter.filter_code).cyan())
|
||||
} else if let Some(filter) = self.as_any().downcast_ref::<SimilarityFilter>() {
|
||||
|
||||
@@ -612,7 +612,7 @@ pub fn parse_url_with_raw_path(url: &str) -> Result<Url> {
|
||||
if let Some(port) = parsed.port() {
|
||||
// if the url has a port, then the farthest right authority component is
|
||||
// the port
|
||||
farthest_right_authority_part = format!(":{}", port);
|
||||
farthest_right_authority_part = format!(":{port}");
|
||||
} else if parsed.has_host() {
|
||||
// if the url has a host, then the farthest right authority component is
|
||||
// the host
|
||||
|
||||
@@ -337,7 +337,7 @@ fn heuristics_wildcard_test_that_auto_filtering_403s_still_allows_for_recursion_
|
||||
});
|
||||
|
||||
srv.mock(|when, then| {
|
||||
when.method(GET).path(format!("/LICENSE/{}", super_long));
|
||||
when.method(GET).path(format!("/LICENSE/{super_long}"));
|
||||
then.status(200);
|
||||
});
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ fn auto_bail_cancels_scan_with_timeouts() {
|
||||
.success();
|
||||
|
||||
let debug_log = read_to_string(logfile).unwrap();
|
||||
let re = Regex::new("total_expected: ([0-9]+),").unwrap();
|
||||
|
||||
// read debug log to get the number of errors enforced
|
||||
for line in debug_log.lines() {
|
||||
@@ -83,7 +84,6 @@ fn auto_bail_cancels_scan_with_timeouts() {
|
||||
let str_msg = message.as_str().unwrap_or_default().to_string();
|
||||
|
||||
if str_msg.starts_with("Stats") {
|
||||
let re = Regex::new("total_expected: ([0-9]+),").unwrap();
|
||||
assert!(re.is_match(&str_msg));
|
||||
let total_expected = re
|
||||
.captures(&str_msg)
|
||||
@@ -154,6 +154,7 @@ fn auto_bail_cancels_scan_with_403s() {
|
||||
|
||||
println!("log filesize: {}", logfile.metadata().unwrap().len());
|
||||
let debug_log = read_to_string(logfile).unwrap();
|
||||
let re = Regex::new("total_expected: ([0-9]+),").unwrap();
|
||||
|
||||
// read debug log to get the number of errors enforced
|
||||
for line in debug_log.lines() {
|
||||
@@ -163,7 +164,6 @@ fn auto_bail_cancels_scan_with_403s() {
|
||||
|
||||
if str_msg.starts_with("Stats") {
|
||||
println!("{str_msg}");
|
||||
let re = Regex::new("total_expected: ([0-9]+),").unwrap();
|
||||
assert!(re.is_match(&str_msg));
|
||||
let total_expected = re
|
||||
.captures(&str_msg)
|
||||
@@ -236,6 +236,7 @@ fn auto_bail_cancels_scan_with_429s() {
|
||||
|
||||
println!("log filesize: {}", logfile.metadata().unwrap().len());
|
||||
let debug_log = read_to_string(logfile).unwrap();
|
||||
let re = Regex::new("total_expected: ([0-9]+),").unwrap();
|
||||
|
||||
// read debug log to get the number of errors enforced
|
||||
for line in debug_log.lines() {
|
||||
@@ -245,7 +246,6 @@ fn auto_bail_cancels_scan_with_429s() {
|
||||
|
||||
if str_msg.starts_with("Stats") {
|
||||
println!("{str_msg}");
|
||||
let re = Regex::new("total_expected: ([0-9]+),").unwrap();
|
||||
assert!(re.is_match(&str_msg));
|
||||
let total_expected = re
|
||||
.captures(&str_msg)
|
||||
|
||||
Reference in New Issue
Block a user