use tracing::warn;
#[must_use]
pub fn strip_range_prefix(req_str: &str) -> &str {
req_str.trim_start_matches(|c: char| !c.is_ascii_digit())
}
pub async fn collect_task_results<T>(handles: Vec<tokio::task::JoinHandle<T>>) -> Vec<T> {
let mut results = Vec::with_capacity(handles.len());
for handle in handles {
match handle.await {
Ok(result) => results.push(result),
Err(e) => warn!("task join error: {e}"),
}
}
results
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[rstest]
#[case::caret("^1.2.3", "1.2.3")]
#[case::tilde("~1.0", "1.0")]
#[case::gte(">=2.0.0", "2.0.0")]
#[case::exact("=1.0.0", "1.0.0")]
#[case::plain("1.0.0", "1.0.0")]
#[case::star_yields_empty("*", "")]
#[case::empty_input("", "")]
fn strip_range_prefix_cases(#[case] input: &str, #[case] expected: &str) {
assert_eq!(strip_range_prefix(input), expected);
}
#[tokio::test]
async fn test_collect_task_results_drops_panicked() {
let prev_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
let handles = vec![
tokio::spawn(async { 1_usize }),
tokio::spawn(async { panic!("simulated join error") }),
tokio::spawn(async { 3_usize }),
];
let results = collect_task_results(handles).await;
std::panic::set_hook(prev_hook);
assert_eq!(results.len(), 2);
}
}