pub trait HasChildren: Clone {
fn children(&self) -> Option<&Vec<Self>>;
fn take_children(self) -> Vec<Self>;
fn is_leaf(&self) -> bool {
self.children().is_none()
}
}
pub trait HasProgress {
fn progress(&self) -> f64;
}
pub fn calculate_overall_progress<T: HasProgress>(items: &[T]) -> f64 {
if items.is_empty() {
return 0.0;
}
let total: f64 = items.iter().map(|i| i.progress()).sum();
if total > 0.0 {
total / items.len() as f64
} else {
0.0
}
}
pub fn flatten_items<T: HasChildren>(items: Vec<T>) -> Vec<T> {
let nested: Vec<Vec<T>> = items
.iter()
.map(|i| i.clone().take_children())
.collect();
let mut children: Vec<T> = nested.into_iter().flatten().collect();
let single_files: Vec<T> = items
.into_iter()
.filter(|i| i.is_leaf())
.collect();
children.extend(single_files);
children
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Debug, PartialEq)]
struct TestItem {
name: String,
children: Option<Vec<TestItem>>,
}
impl HasChildren for TestItem {
fn children(&self) -> Option<&Vec<Self>> {
self.children.as_ref()
}
fn take_children(self) -> Vec<Self> {
self.children.unwrap_or_default()
}
}
#[test]
fn test_flatten_items_with_no_children() {
let items = vec![
TestItem {
name: "file1".into(),
children: None,
},
TestItem {
name: "file2".into(),
children: None,
},
];
let result = flatten_items(items.clone());
assert_eq!(result.len(), 2);
assert!(result.iter().any(|i| i.name == "file1"));
assert!(result.iter().any(|i| i.name == "file2"));
}
#[test]
fn test_flatten_items_with_children() {
let child1 = TestItem {
name: "child1".into(),
children: None,
};
let child2 = TestItem {
name: "child2".into(),
children: None,
};
let parent = TestItem {
name: "parent".into(),
children: Some(vec![child1.clone(), child2.clone()]),
};
let single = TestItem {
name: "single".into(),
children: None,
};
let items = vec![parent, single.clone()];
let result = flatten_items(items);
assert_eq!(result.len(), 3);
assert!(result.iter().any(|i| i.name == "child1"));
assert!(result.iter().any(|i| i.name == "child2"));
assert!(result.iter().any(|i| i.name == "single"));
}
#[test]
fn test_flatten_items_empty() {
let items: Vec<TestItem> = vec![];
let result = flatten_items(items);
assert!(result.is_empty());
}
#[test]
fn test_is_leaf() {
let leaf = TestItem {
name: "leaf".into(),
children: None,
};
let parent = TestItem {
name: "parent".into(),
children: Some(vec![]),
};
assert!(leaf.is_leaf());
assert!(!parent.is_leaf());
}
#[derive(Clone)]
struct ProgressItem {
progress: f64,
}
impl HasProgress for ProgressItem {
fn progress(&self) -> f64 {
self.progress
}
}
#[test]
fn test_calculate_overall_progress_empty() {
let items: Vec<ProgressItem> = vec![];
assert_eq!(calculate_overall_progress(&items), 0.0);
}
#[test]
fn test_calculate_overall_progress_single_item() {
let items = vec![ProgressItem { progress: 50.0 }];
assert_eq!(calculate_overall_progress(&items), 50.0);
}
#[test]
fn test_calculate_overall_progress_multiple_items() {
let items = vec![
ProgressItem { progress: 25.0 },
ProgressItem { progress: 75.0 },
];
assert_eq!(calculate_overall_progress(&items), 50.0);
}
#[test]
fn test_calculate_overall_progress_all_zero() {
let items = vec![
ProgressItem { progress: 0.0 },
ProgressItem { progress: 0.0 },
];
assert_eq!(calculate_overall_progress(&items), 0.0);
}
#[test]
fn test_calculate_overall_progress_all_complete() {
let items = vec![
ProgressItem { progress: 100.0 },
ProgressItem { progress: 100.0 },
ProgressItem { progress: 100.0 },
];
assert_eq!(calculate_overall_progress(&items), 100.0);
}
}