use std::collections::HashSet; use super::funejson::Value; use super::todo_item::Todo; use super::todo_item_validate_todo::{todos_from_value, todos_to_value}; /// Manual order: order ascending, ties by position (sort_by_key is stable). fn in_manual_order(todos: &[Todo], which: &str) -> Vec { let mut seen: HashSet<&str> = HashSet::new(); for todo in todos { if !seen.insert(todo.id.as_str()) { panic!("duplicate id \"{}\" in the {} list", todo.id, which); } } let mut sorted = todos.to_vec(); sorted.sort_by_key(|todo| todo.order); sorted } /// Merge by id: a todo in both lists is taken from `incoming`, in the place /// the current one had; todos only in `incoming` follow in their own manual /// order. The result is in manual order, renumbered 0 to n-1. /// /// # Panics /// On an id that appears twice in either list. pub fn merge_todos(current: &[Todo], incoming: &[Todo]) -> Vec { let mine = in_manual_order(current, "current"); let theirs = in_manual_order(incoming, "incoming"); let kept: HashSet<&str> = mine.iter().map(|todo| todo.id.as_str()).collect(); let mut merged: Vec = mine .iter() .map(|todo| theirs.iter().find(|t| t.id == todo.id).unwrap_or(todo).clone()) .collect(); merged.extend(theirs.iter().filter(|todo| !kept.contains(todo.id.as_str())).cloned()); for (order, todo) in merged.iter_mut().enumerate() { todo.order = order as i64; } merged } pub fn fune_vector(args: &[Value]) -> Value { todos_to_value(&merge_todos(&todos_from_value(&args[0]), &todos_from_value(&args[1]))) }