use iced::Element; use itertools::Itertools; use crate::traits::Focusable; use super::{Message, Navigation}; #[derive(PartialEq)] pub enum ReplaceOptionResult { ReplacedOption, NoSuchOption, } impl<'a, TOption> Navigation where TOption: Clone + Focusable + PartialEq + 'a, Element<'a, Message>: From<&'a TOption>, { pub fn replace_first

(&'a mut self, pred: P, new: &'a Vec) -> ReplaceOptionResult where P: Fn(&TOption) -> bool, { if self.replace_first_option_before_active(&pred, new) == ReplaceOptionResult::ReplacedOption { ReplaceOptionResult::ReplacedOption } else if self.replace_active_option_if(&pred, new) == ReplaceOptionResult::ReplacedOption { ReplaceOptionResult::ReplacedOption } else if self.replace_first_option_after_active(pred, new) == ReplaceOptionResult::ReplacedOption { ReplaceOptionResult::ReplacedOption } else { ReplaceOptionResult::NoSuchOption } } fn replace_first_option_before_active

( &mut self, pred: P, new: &'a Vec, ) -> ReplaceOptionResult where P: Fn(&TOption) -> bool, { if let Some((existing_option_index, _)) = self .options_before_active .iter() .find_position(|option| pred(*option)) { self.options_before_active.splice( existing_option_index..(existing_option_index + 1), new.clone(), ); ReplaceOptionResult::ReplacedOption } else { ReplaceOptionResult::NoSuchOption } } fn replace_first_option_after_active

( &mut self, pred: P, new: &'a Vec, ) -> ReplaceOptionResult where P: Fn(&TOption) -> bool, { if let Some((existing_option_index, _)) = self .options_after_active .iter() .find_position(|option| pred(*option)) { self.options_after_active.splice( existing_option_index..(existing_option_index + 1), new.clone(), ); ReplaceOptionResult::ReplacedOption } else { ReplaceOptionResult::NoSuchOption } } fn replace_active_option_if

(&mut self, pred: P, new: &'a Vec) -> ReplaceOptionResult where P: Fn(&TOption) -> bool, { if self .active_option .as_ref() .is_none_or(|active_option| !pred(active_option)) { return ReplaceOptionResult::NoSuchOption; } self.active_option = new.first().cloned(); self.active_option.iter_mut().for_each(|o| o.focus()); self.options_after_active .splice(0..0, new.split_at(1).1.to_vec()); ReplaceOptionResult::ReplacedOption } } #[cfg(test)] mod test { use itertools::Itertools; use crate::{ gui::components::{ navigation::{self, dummy_navigation_option::DummyNavigationOption}, Navigation, }, traits::Component, }; macro_rules! assert_active_option_eq { ($nav:expr, $option:expr) => { if !$nav .active_option .as_ref() .is_some_and(|o| o.name == $option) { panic!( "navigation active option should have been Some(\"{}\"), but was {:?}", $option, $nav.active_option ); } }; } macro_rules! assert_options_before_eq { ($nav:expr, $options:expr) => { let options_before_eq = $options.len() == $nav.options_before_active.len() && (|| { (0..$options.len()).all(|i| $options[i] == $nav.options_before_active[i].name) })(); if !options_before_eq { panic!( "navigation should have had options ({}) before active option, but were ({})", $options.iter().join(", "), $nav.options_before_active.iter().join(", "), ); } }; } macro_rules! assert_options_after_eq { ($nav:expr, $options:expr) => { let options_after_eq = $options.len() == $nav.options_after_active.len() && (|| { (0..$options.len()).all(|i| $options[i] == $nav.options_after_active[i].name) })(); if !options_after_eq { panic!( "navigation should have had options ({}) after active option, but were ({})", $options.iter().join(", "), $nav.options_after_active.iter().join(", "), ); } }; } fn make_dummy_nav_with_options_a_to_g() -> Navigation { Navigation::new( None, vec![ DummyNavigationOption::new("a"), DummyNavigationOption::new("b"), DummyNavigationOption::new("c"), DummyNavigationOption::new("d"), DummyNavigationOption::new("e"), DummyNavigationOption::new("f"), DummyNavigationOption::new("g"), ], ) } fn select_dummy_nav_option(nav: &mut Navigation, option: &str) { nav.update(navigation::Message::SelectOption( DummyNavigationOption::new(option), )); } #[test] fn select_dummy_nav_option_selects_dummy_nav_option() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); assert_options_before_eq!(nav, ["a", "b", "c"]); assert_active_option_eq!(nav, "d"); assert_options_after_eq!(nav, ["e", "f", "g"]); } #[test] fn replace_first_option_before_active_with_one() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); let new = vec![DummyNavigationOption::new("z")]; nav.replace_first(|o| o.name == "a", &new); assert_options_before_eq!(nav, ["z", "b", "c"]); assert_active_option_eq!(nav, "d"); assert_options_after_eq!(nav, ["e", "f", "g"]); } #[test] fn replace_first_option_before_active_with_many() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); let new = vec![ DummyNavigationOption::new("x"), DummyNavigationOption::new("y"), DummyNavigationOption::new("z"), ]; nav.replace_first(|o| o.name == "a", &new); assert_options_before_eq!(nav, ["x", "y", "z", "b", "c"]); assert_active_option_eq!(nav, "d"); assert_options_after_eq!(nav, ["e", "f", "g"]); } #[test] fn replace_last_option_before_active_with_one() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); let new = vec![DummyNavigationOption::new("z")]; nav.replace_first(|o| o.name == "c", &new); assert_options_before_eq!(nav, ["a", "b", "z"]); assert_active_option_eq!(nav, "d"); assert_options_after_eq!(nav, ["e", "f", "g"]); } #[test] fn replace_last_option_before_active_with_many() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); let new = vec![ DummyNavigationOption::new("x"), DummyNavigationOption::new("y"), DummyNavigationOption::new("z"), ]; nav.replace_first(|o| o.name == "c", &new); assert_options_before_eq!(nav, ["a", "b", "x", "y", "z"]); assert_active_option_eq!(nav, "d"); assert_options_after_eq!(nav, ["e", "f", "g"]); } #[test] fn replace_active_option_with_one() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); let new = vec![DummyNavigationOption::new("z")]; nav.replace_first(|o| o.name == "d", &new); assert_options_before_eq!(nav, ["a", "b", "c"]); assert_active_option_eq!(nav, "z"); assert_options_after_eq!(nav, ["e", "f", "g"]); } #[test] fn replace_active_option_with_one_when_active_option_is_first() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "a"); let new = vec![DummyNavigationOption::new("z")]; nav.replace_first(|o| o.name == "a", &new); assert_options_before_eq!(nav, [] as [String; 0]); assert_active_option_eq!(nav, "z"); assert_options_after_eq!(nav, ["b", "c", "d", "e", "f", "g"]); } #[test] fn replace_active_option_with_one_when_active_option_is_last() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "g"); let new = vec![DummyNavigationOption::new("z")]; nav.replace_first(|o| o.name == "g", &new); assert_options_before_eq!(nav, ["a", "b", "c", "d", "e", "f"]); assert_active_option_eq!(nav, "z"); assert_options_after_eq!(nav, [] as [String; 0]); } #[test] fn replace_active_option_with_one_when_active_option_is_only() { let active_option = DummyNavigationOption::new("a"); let mut nav = Navigation::new(Some(active_option.clone()), vec![active_option]); let new = vec![DummyNavigationOption::new("z")]; nav.replace_first(|o| o.name == "a", &new); assert_options_before_eq!(nav, [] as [String; 0]); assert_active_option_eq!(nav, "z"); assert_options_after_eq!(nav, [] as [String; 0]); } #[test] fn replace_active_option_with_many() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); let new = vec![ DummyNavigationOption::new("x"), DummyNavigationOption::new("y"), DummyNavigationOption::new("z"), ]; nav.replace_first(|o| o.name == "d", &new); assert_options_before_eq!(nav, ["a", "b", "c"]); assert_active_option_eq!(nav, "x"); assert_options_after_eq!(nav, ["y", "z", "e", "f", "g"]); } #[test] fn replace_active_option_with_many_when_active_option_is_first() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "a"); let new = vec![ DummyNavigationOption::new("x"), DummyNavigationOption::new("y"), DummyNavigationOption::new("z"), ]; nav.replace_first(|o| o.name == "a", &new); assert_options_before_eq!(nav, [] as [String; 0]); assert_active_option_eq!(nav, "x"); assert_options_after_eq!(nav, ["y", "z", "b", "c", "d", "e", "f", "g"]); } #[test] fn replace_active_option_with_many_when_active_option_is_last() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "g"); let new = vec![ DummyNavigationOption::new("x"), DummyNavigationOption::new("y"), DummyNavigationOption::new("z"), ]; nav.replace_first(|o| o.name == "g", &new); assert_options_before_eq!(nav, ["a", "b", "c", "d", "e", "f"]); assert_active_option_eq!(nav, "x"); assert_options_after_eq!(nav, ["y", "z"]); } #[test] fn replace_active_option_with_many_when_active_option_is_only() { let active_option = DummyNavigationOption::new("a"); let mut nav = Navigation::new(Some(active_option.clone()), vec![active_option]); let new = vec![ DummyNavigationOption::new("x"), DummyNavigationOption::new("y"), DummyNavigationOption::new("z"), ]; nav.replace_first(|o| o.name == "a", &new); assert_options_before_eq!(nav, [] as [String; 0]); assert_active_option_eq!(nav, "x"); assert_options_after_eq!(nav, ["y", "z"]); } #[test] fn replace_first_option_after_active_with_one() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); let new = vec![DummyNavigationOption::new("z")]; nav.replace_first(|o| o.name == "e", &new); assert_options_before_eq!(nav, ["a", "b", "c"]); assert_active_option_eq!(nav, "d"); assert_options_after_eq!(nav, ["z", "f", "g"]); } #[test] fn replace_first_option_after_active_with_many() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); let new = vec![ DummyNavigationOption::new("x"), DummyNavigationOption::new("y"), DummyNavigationOption::new("z"), ]; nav.replace_first(|o| o.name == "e", &new); assert_options_before_eq!(nav, ["a", "b", "c"]); assert_active_option_eq!(nav, "d"); assert_options_after_eq!(nav, ["x", "y", "z", "f", "g"]); } #[test] fn replace_last_option_after_active_with_one() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); let new = vec![DummyNavigationOption::new("z")]; nav.replace_first(|o| o.name == "g", &new); assert_options_before_eq!(nav, ["a", "b", "c"]); assert_active_option_eq!(nav, "d"); assert_options_after_eq!(nav, ["e", "f", "z"]); } #[test] fn replace_last_option_after_active_with_many() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); let new = vec![ DummyNavigationOption::new("x"), DummyNavigationOption::new("y"), DummyNavigationOption::new("z"), ]; nav.replace_first(|o| o.name == "g", &new); assert_options_before_eq!(nav, ["a", "b", "c"]); assert_active_option_eq!(nav, "d"); assert_options_after_eq!(nav, ["e", "f", "x", "y", "z"]); } #[test] fn replace_first_option_before_active_when_duplicates() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); nav.replace_first(|o| o.name == "c", &vec![DummyNavigationOption::new("a")]); let new = vec![DummyNavigationOption::new("z")]; nav.replace_first(|o| o.name == "a", &new); assert_options_before_eq!(nav, ["z", "b", "a"]); assert_active_option_eq!(nav, "d"); assert_options_after_eq!(nav, ["e", "f", "g"]); } #[test] fn replace_first_option_before_active_when_duplicate_in_active_option() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); nav.replace_first(|o| o.name == "d", &vec![DummyNavigationOption::new("a")]); let new = vec![DummyNavigationOption::new("z")]; nav.replace_first(|o| o.name == "a", &new); assert_options_before_eq!(nav, ["z", "b", "c"]); assert_active_option_eq!(nav, "a"); assert_options_after_eq!(nav, ["e", "f", "g"]); } #[test] fn replace_first_option_before_active_when_duplicate_in_options_after_active() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); nav.replace_first(|o| o.name == "f", &vec![DummyNavigationOption::new("a")]); let new = vec![DummyNavigationOption::new("z")]; nav.replace_first(|o| o.name == "a", &new); assert_options_before_eq!(nav, ["z", "b", "c"]); assert_active_option_eq!(nav, "d"); assert_options_after_eq!(nav, ["e", "a", "g"]); } #[test] fn replace_active_option_when_duplicate_in_options_after_active() { let mut nav = make_dummy_nav_with_options_a_to_g(); select_dummy_nav_option(&mut nav, "d"); nav.replace_first(|o| o.name == "f", &vec![DummyNavigationOption::new("d")]); let new = vec![DummyNavigationOption::new("z")]; nav.replace_first(|o| o.name == "d", &new); assert_options_before_eq!(nav, ["a", "b", "c"]); assert_active_option_eq!(nav, "z"); assert_options_after_eq!(nav, ["e", "d", "g"]); } }