use indicatrix_editor::optimize_view::{
DEFAULT_BUDGET, DEFAULT_CANDIDATES, DEFAULT_STARTS, RangeInput, parse_budget,
};
#[derive(Debug, Clone, Copy)]
pub(super) struct Tick {
pub on: bool,
pub default_on: bool,
}
impl Tick {
const fn moved(self) -> bool {
self.on != self.default_on
}
}
#[derive(Debug, Clone, Copy)]
pub(super) struct HiddenSettings<'a> {
pub vary_anchored: Tick,
pub keep_girdle: Tick,
pub polish: Tick,
pub only_selected: Tick,
pub ranges_customised: bool,
pub budget_text: &'a str,
pub seed_text: &'a str,
pub candidates: i32,
pub starts: i32,
}
pub(super) fn settings_in_use(settings: &HiddenSettings<'_>) -> bool {
settings.vary_anchored.moved()
|| settings.keep_girdle.moved()
|| settings.polish.moved()
|| settings.only_selected.moved()
|| settings.ranges_customised
|| parse_budget(settings.budget_text) != Ok(DEFAULT_BUDGET)
|| !seed_is_default(settings.seed_text)
|| usize::try_from(settings.candidates) != Ok(DEFAULT_CANDIDATES)
|| usize::try_from(settings.starts) != Ok(DEFAULT_STARTS)
}
fn seed_is_default(text: &str) -> bool {
let text = text.trim();
text.is_empty() || text.parse::<u64>() == Ok(0)
}
pub(super) fn ranges_customised(inputs: &[RangeInput]) -> bool {
inputs
.iter()
.any(|input| !input.min_text.trim().is_empty() || !input.max_text.trim().is_empty())
}
pub(super) fn simple_labels<'a>(labels: &[&'a str]) -> Vec<&'a str> {
labels
.split_last()
.map_or_else(Vec::new, |(_custom, rest)| rest.to_vec())
}
#[cfg(test)]
mod tests {
use super::*;
const fn tick(on: bool, default_on: bool) -> Tick {
Tick { on, default_on }
}
fn defaults() -> HiddenSettings<'static> {
HiddenSettings {
vary_anchored: tick(false, false),
keep_girdle: tick(true, true),
polish: tick(true, true),
only_selected: tick(false, false),
ranges_customised: false,
budget_text: "800",
seed_text: "0",
candidates: 3,
starts: 8,
}
}
#[test]
fn a_fresh_tab_has_nothing_in_use() {
assert!(!settings_in_use(&defaults()));
}
#[test]
fn the_vary_tick_is_judged_against_the_designs_default() {
let imported = HiddenSettings {
vary_anchored: tick(true, true),
..defaults()
};
assert!(!settings_in_use(&imported));
let unticked = HiddenSettings {
vary_anchored: tick(false, true),
..defaults()
};
assert!(settings_in_use(&unticked));
let ticked = HiddenSettings {
vary_anchored: tick(true, false),
..defaults()
};
assert!(settings_in_use(&ticked));
}
#[test]
fn each_other_hidden_setting_counts_when_it_moves() {
let cases = [
HiddenSettings {
keep_girdle: tick(false, true),
..defaults()
},
HiddenSettings {
ranges_customised: true,
..defaults()
},
HiddenSettings {
budget_text: "400",
..defaults()
},
HiddenSettings {
seed_text: "7",
..defaults()
},
HiddenSettings {
candidates: 5,
..defaults()
},
HiddenSettings {
starts: 3,
..defaults()
},
HiddenSettings {
starts: 1,
..defaults()
},
HiddenSettings {
polish: tick(false, true),
..defaults()
},
HiddenSettings {
only_selected: tick(true, false),
..defaults()
},
];
for case in cases {
assert!(settings_in_use(&case), "{case:?}");
}
}
#[test]
fn a_blank_or_calculated_budget_and_a_blank_seed_read_like_the_defaults() {
let blank_budget = HiddenSettings {
budget_text: " ",
..defaults()
};
assert!(!settings_in_use(&blank_budget));
let calculated_text = format!("{} * 2", DEFAULT_BUDGET / 2);
let calculated = HiddenSettings {
budget_text: &calculated_text,
..defaults()
};
assert!(!settings_in_use(&calculated));
let blank_seed = HiddenSettings {
seed_text: "",
..defaults()
};
assert!(!settings_in_use(&blank_seed));
let padded_zero = HiddenSettings {
seed_text: " 00 ",
..defaults()
};
assert!(!settings_in_use(&padded_zero));
}
#[test]
fn a_budget_that_cannot_be_read_counts_because_the_box_is_hidden() {
let broken = HiddenSettings {
budget_text: "lots",
..defaults()
};
assert!(settings_in_use(&broken));
let negative_seed = HiddenSettings {
seed_text: "-1",
..defaults()
};
assert!(settings_in_use(&negative_seed));
}
#[test]
fn typed_ranges_count_only_when_something_was_typed() {
assert!(!ranges_customised(&[]));
let blank = RangeInput {
tier: 1,
min_text: " ".to_string(),
max_text: String::new(),
};
assert!(!ranges_customised(std::slice::from_ref(&blank)));
let typed = RangeInput {
tier: 2,
min_text: "35".to_string(),
max_text: String::new(),
};
assert!(ranges_customised(&[blank, typed]));
}
#[test]
fn the_simple_list_drops_custom_and_keeps_the_positions() {
let all = ["Balanced", "Windowing", "Custom"];
assert_eq!(simple_labels(&all), vec!["Balanced", "Windowing"]);
assert_eq!(simple_labels(&[]).len(), 0);
assert_eq!(simple_labels(&["Custom"]).len(), 0);
}
}