use serde::{Deserialize, Serialize};
fn default_enabled() -> bool {
true
}
fn default_batch_size() -> usize {
100
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ParallelConfig {
#[serde(default = "default_enabled")]
pub enabled: bool,
#[serde(default)]
pub max_concurrency: Option<usize>,
#[serde(default = "default_batch_size_option")]
pub batch_size: Option<usize>,
}
fn default_batch_size_option() -> Option<usize> {
Some(default_batch_size())
}
impl Default for ParallelConfig {
fn default() -> Self {
Self {
enabled: default_enabled(),
max_concurrency: None,
batch_size: Some(default_batch_size()),
}
}
}
impl ParallelConfig {
pub fn new() -> Self {
Self::default()
}
pub fn sequential() -> Self {
Self {
enabled: false,
..Default::default()
}
}
pub fn effective_concurrency(&self) -> usize {
self.max_concurrency.unwrap_or_else(num_cpus)
}
pub fn effective_batch_size(&self) -> usize {
self.batch_size.unwrap_or(default_batch_size())
}
}
fn num_cpus() -> usize {
std::thread::available_parallelism()
.map(|p| p.get())
.unwrap_or(1)
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct BatchAnalysisConfig {
#[serde(default)]
pub parallelism: ParallelConfig,
#[serde(default)]
pub fail_fast: bool,
#[serde(default)]
pub collect_timing: bool,
}
impl BatchAnalysisConfig {
pub fn accumulating() -> Self {
Self {
parallelism: ParallelConfig::default(),
fail_fast: false,
collect_timing: false,
}
}
pub fn fail_fast() -> Self {
Self {
parallelism: ParallelConfig::default(),
fail_fast: true,
collect_timing: false,
}
}
pub fn with_timing(mut self) -> Self {
self.collect_timing = true;
self
}
pub fn sequential(mut self) -> Self {
self.parallelism = ParallelConfig::sequential();
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parallel_config_default() {
let config = ParallelConfig::default();
assert!(config.enabled);
assert!(config.max_concurrency.is_none());
assert_eq!(config.batch_size, Some(100));
}
#[test]
fn test_parallel_config_sequential() {
let config = ParallelConfig::sequential();
assert!(!config.enabled);
}
#[test]
fn test_effective_concurrency() {
let config = ParallelConfig {
enabled: true,
max_concurrency: Some(4),
batch_size: None,
};
assert_eq!(config.effective_concurrency(), 4);
let config = ParallelConfig::default();
assert!(config.effective_concurrency() >= 1);
}
#[test]
fn test_effective_batch_size() {
let config = ParallelConfig::default();
assert_eq!(config.effective_batch_size(), 100);
let config = ParallelConfig {
batch_size: Some(50),
..Default::default()
};
assert_eq!(config.effective_batch_size(), 50);
let config = ParallelConfig {
batch_size: None,
..Default::default()
};
assert_eq!(config.effective_batch_size(), 100);
}
#[test]
fn test_batch_config_default() {
let config = BatchAnalysisConfig::default();
assert!(!config.fail_fast);
assert!(!config.collect_timing);
assert!(config.parallelism.enabled);
}
#[test]
fn test_batch_config_accumulating() {
let config = BatchAnalysisConfig::accumulating();
assert!(!config.fail_fast);
}
#[test]
fn test_batch_config_fail_fast() {
let config = BatchAnalysisConfig::fail_fast();
assert!(config.fail_fast);
}
#[test]
fn test_batch_config_with_timing() {
let config = BatchAnalysisConfig::default().with_timing();
assert!(config.collect_timing);
}
#[test]
fn test_batch_config_sequential() {
let config = BatchAnalysisConfig::default().sequential();
assert!(!config.parallelism.enabled);
}
#[test]
fn test_parallel_config_serde() {
let config = ParallelConfig {
enabled: true,
max_concurrency: Some(8),
batch_size: Some(200),
};
let json = serde_json::to_string(&config).unwrap();
let parsed: ParallelConfig = serde_json::from_str(&json).unwrap();
assert_eq!(config, parsed);
}
#[test]
fn test_batch_config_serde() {
let config = BatchAnalysisConfig {
parallelism: ParallelConfig::default(),
fail_fast: true,
collect_timing: true,
};
let json = serde_json::to_string(&config).unwrap();
let parsed: BatchAnalysisConfig = serde_json::from_str(&json).unwrap();
assert_eq!(config, parsed);
}
}