use rayon::prelude::*;
use crate::error::ParserError;
use crate::parser::parse;
use crate::Molecule;
pub fn parse_batch(inputs: &[&str]) -> Vec<Result<Molecule, ParserError>> {
inputs.par_iter().map(|s| parse(s)).collect()
}
pub fn parse_batch_ok(inputs: &[&str]) -> Vec<Molecule> {
inputs.par_iter().filter_map(|s| parse(s).ok()).collect()
}
pub fn parse_batch_indexed(inputs: &[&str]) -> Vec<(usize, Result<Molecule, ParserError>)> {
inputs
.par_iter()
.enumerate()
.map(|(i, s)| (i, parse(s)))
.collect()
}
#[derive(Debug, Clone, Default)]
pub struct BatchParseStats {
pub success_count: usize,
pub error_count: usize,
pub total_count: usize,
}
impl BatchParseStats {
pub fn success_rate(&self) -> f64 {
if self.total_count == 0 {
0.0
} else {
(self.success_count as f64 / self.total_count as f64) * 100.0
}
}
}
pub fn parse_batch_with_stats(
inputs: &[&str],
) -> (Vec<Molecule>, Vec<(usize, ParserError)>, BatchParseStats) {
let results: Vec<(usize, Result<Molecule, ParserError>)> = inputs
.par_iter()
.enumerate()
.map(|(i, s)| (i, parse(s)))
.collect();
let mut molecules = Vec::new();
let mut errors = Vec::new();
for (i, result) in results {
match result {
Ok(mol) => molecules.push(mol),
Err(e) => errors.push((i, e)),
}
}
let stats = BatchParseStats {
success_count: molecules.len(),
error_count: errors.len(),
total_count: inputs.len(),
};
(molecules, errors, stats)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_batch() {
let inputs = vec!["C", "CC", "CCC", "CCCC"];
let results = parse_batch(&inputs);
assert_eq!(results.len(), 4);
for result in &results {
assert!(result.is_ok());
}
}
#[test]
fn test_parse_batch_with_errors() {
let inputs = vec!["C", "invalid[", "CC"];
let results = parse_batch(&inputs);
assert_eq!(results.len(), 3);
assert!(results[0].is_ok());
assert!(results[1].is_err());
assert!(results[2].is_ok());
}
#[test]
fn test_parse_batch_ok() {
let inputs = vec!["C", "invalid[", "CC", "also-bad"];
let molecules = parse_batch_ok(&inputs);
assert_eq!(molecules.len(), 2);
}
#[test]
fn test_parse_batch_with_stats() {
let inputs = vec!["C", "CC", "bad[", "CCC"];
let (molecules, errors, stats) = parse_batch_with_stats(&inputs);
assert_eq!(molecules.len(), 3);
assert_eq!(errors.len(), 1);
assert_eq!(errors[0].0, 2); assert_eq!(stats.success_count, 3);
assert_eq!(stats.error_count, 1);
assert_eq!(stats.total_count, 4);
assert!((stats.success_rate() - 75.0).abs() < f64::EPSILON);
}
#[test]
fn test_parse_complex_molecules() {
let inputs = vec![
"c1ccccc1", "CC(=O)O", "CCO", "C1CC1", "CC(C)(C)C", "c1ccc2ccccc2c1", "CC(C)CC(C)(C)C", ];
let results = parse_batch(&inputs);
for (i, result) in results.iter().enumerate() {
assert!(result.is_ok(), "Failed to parse index {}: {:?}", i, result);
}
}
#[test]
fn test_parse_batch_indexed() {
let inputs = vec!["C", "bad[", "CC", "also-bad", "CCC"];
let results = parse_batch_indexed(&inputs);
assert_eq!(results.len(), 5);
let (idx0, res0) = &results[0];
assert_eq!(*idx0, 0);
assert!(res0.is_ok());
let (idx1, res1) = &results[1];
assert_eq!(*idx1, 1);
assert!(res1.is_err());
let (idx2, res2) = &results[2];
assert_eq!(*idx2, 2);
assert!(res2.is_ok());
let (idx3, res3) = &results[3];
assert_eq!(*idx3, 3);
assert!(res3.is_err());
let (idx4, res4) = &results[4];
assert_eq!(*idx4, 4);
assert!(res4.is_ok());
}
#[test]
fn test_parse_batch_empty_input() {
let inputs: Vec<&str> = vec![];
let results = parse_batch(&inputs);
assert!(results.is_empty());
let molecules = parse_batch_ok(&inputs);
assert!(molecules.is_empty());
let (molecules, errors, stats) = parse_batch_with_stats(&inputs);
assert!(molecules.is_empty());
assert!(errors.is_empty());
assert_eq!(stats.total_count, 0);
assert_eq!(stats.success_rate(), 0.0);
}
#[test]
fn test_parse_batch_all_invalid() {
let inputs = vec!["bad[", "also-bad", "[invalid"];
let (molecules, errors, stats) = parse_batch_with_stats(&inputs);
assert!(molecules.is_empty());
assert_eq!(errors.len(), 3);
assert_eq!(stats.success_count, 0);
assert_eq!(stats.error_count, 3);
assert_eq!(stats.success_rate(), 0.0);
}
#[test]
fn test_batch_parse_stats_success_rate() {
let stats = BatchParseStats {
success_count: 3,
error_count: 1,
total_count: 4,
};
assert!((stats.success_rate() - 75.0).abs() < f64::EPSILON);
let empty_stats = BatchParseStats::default();
assert_eq!(empty_stats.success_rate(), 0.0);
}
}