use serde_json::Value;
#[derive(Debug, Clone, PartialEq)]
pub enum Phase {
Setup,
Map,
Reduce,
}
pub fn calculate_parallelism(total_items: usize, max_parallel: usize) -> usize {
if total_items == 0 {
return 0;
}
std::cmp::min(total_items, max_parallel)
}
pub fn plan_execution_phases(has_setup: bool, has_reduce: bool) -> Vec<Phase> {
let mut phases = Vec::new();
if has_setup {
phases.push(Phase::Setup);
}
phases.push(Phase::Map);
if has_reduce {
phases.push(Phase::Reduce);
}
phases
}
pub fn distribute_work(items: Vec<Value>, parallelism: usize) -> Vec<Vec<Value>> {
if items.is_empty() || parallelism == 0 {
return vec![];
}
let chunk_size = items.len().div_ceil(parallelism);
items
.chunks(chunk_size)
.map(|chunk| chunk.to_vec())
.collect()
}
pub fn calculate_batch_size(
total_items: usize,
available_resources: usize,
max_batch: usize,
) -> usize {
if available_resources == 0 {
return 0;
}
let ideal_batch = total_items.div_ceil(available_resources);
std::cmp::min(ideal_batch, max_batch)
}
pub fn should_batch(item_count: usize, threshold: usize) -> bool {
item_count > threshold
}
pub fn sort_by_priority(
mut items: Vec<Value>,
priority_field: &str,
descending: bool,
) -> Vec<Value> {
items.sort_by(|a, b| {
let a_priority = extract_priority(a, priority_field);
let b_priority = extract_priority(b, priority_field);
if descending {
b_priority.cmp(&a_priority)
} else {
a_priority.cmp(&b_priority)
}
});
items
}
fn extract_priority(item: &Value, field: &str) -> i64 {
item.get(field).and_then(|v| v.as_i64()).unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_calculate_parallelism_normal() {
assert_eq!(calculate_parallelism(100, 10), 10);
}
#[test]
fn test_calculate_parallelism_fewer_items_than_max() {
assert_eq!(calculate_parallelism(5, 10), 5);
}
#[test]
fn test_calculate_parallelism_zero_items() {
assert_eq!(calculate_parallelism(0, 10), 0);
}
#[test]
fn test_plan_execution_phases_all() {
let phases = plan_execution_phases(true, true);
assert_eq!(phases.len(), 3);
assert_eq!(phases[0], Phase::Setup);
assert_eq!(phases[1], Phase::Map);
assert_eq!(phases[2], Phase::Reduce);
}
#[test]
fn test_plan_execution_phases_map_only() {
let phases = plan_execution_phases(false, false);
assert_eq!(phases.len(), 1);
assert_eq!(phases[0], Phase::Map);
}
#[test]
fn test_plan_execution_phases_setup_and_map() {
let phases = plan_execution_phases(true, false);
assert_eq!(phases.len(), 2);
assert_eq!(phases[0], Phase::Setup);
assert_eq!(phases[1], Phase::Map);
}
#[test]
fn test_distribute_work_even_distribution() {
let items = vec![json!(1), json!(2), json!(3), json!(4)];
let distributed = distribute_work(items, 2);
assert_eq!(distributed.len(), 2);
assert_eq!(distributed[0].len(), 2);
assert_eq!(distributed[1].len(), 2);
}
#[test]
fn test_distribute_work_uneven_distribution() {
let items = vec![json!(1), json!(2), json!(3), json!(4), json!(5)];
let distributed = distribute_work(items, 2);
assert_eq!(distributed.len(), 2);
assert_eq!(distributed[0].len(), 3);
assert_eq!(distributed[1].len(), 2);
}
#[test]
fn test_distribute_work_empty() {
let distributed = distribute_work(vec![], 2);
assert_eq!(distributed.len(), 0);
}
#[test]
fn test_distribute_work_zero_parallelism() {
let items = vec![json!(1), json!(2)];
let distributed = distribute_work(items, 0);
assert_eq!(distributed.len(), 0);
}
#[test]
fn test_calculate_batch_size_normal() {
assert_eq!(calculate_batch_size(100, 10, 20), 10);
}
#[test]
fn test_calculate_batch_size_exceeds_max() {
assert_eq!(calculate_batch_size(100, 2, 20), 20);
}
#[test]
fn test_calculate_batch_size_zero_resources() {
assert_eq!(calculate_batch_size(100, 0, 20), 0);
}
#[test]
fn test_should_batch_above_threshold() {
assert!(should_batch(100, 50));
}
#[test]
fn test_should_batch_below_threshold() {
assert!(!should_batch(30, 50));
}
#[test]
fn test_should_batch_at_threshold() {
assert!(!should_batch(50, 50));
}
#[test]
fn test_sort_by_priority_descending() {
let items = vec![
json!({"name": "a", "priority": 1}),
json!({"name": "b", "priority": 5}),
json!({"name": "c", "priority": 3}),
];
let sorted = sort_by_priority(items, "priority", true);
assert_eq!(sorted[0].get("priority").unwrap().as_i64().unwrap(), 5);
assert_eq!(sorted[1].get("priority").unwrap().as_i64().unwrap(), 3);
assert_eq!(sorted[2].get("priority").unwrap().as_i64().unwrap(), 1);
}
#[test]
fn test_sort_by_priority_ascending() {
let items = vec![
json!({"name": "a", "priority": 5}),
json!({"name": "b", "priority": 1}),
json!({"name": "c", "priority": 3}),
];
let sorted = sort_by_priority(items, "priority", false);
assert_eq!(sorted[0].get("priority").unwrap().as_i64().unwrap(), 1);
assert_eq!(sorted[1].get("priority").unwrap().as_i64().unwrap(), 3);
assert_eq!(sorted[2].get("priority").unwrap().as_i64().unwrap(), 5);
}
#[test]
fn test_sort_by_priority_missing_field() {
let items = vec![
json!({"name": "a"}),
json!({"name": "b", "priority": 5}),
json!({"name": "c", "priority": 3}),
];
let sorted = sort_by_priority(items, "priority", true);
assert_eq!(sorted[2].get("priority"), None);
}
}