use std::path::PathBuf;
use crate::app::history::NavigationHistory;
use crate::app::sort::SortSettings;
use crate::app::state::Entry;
#[derive(Debug)]
pub struct TabState {
pub cwd: PathBuf,
pub entries: Vec<Entry>,
pub selected: usize,
pub history: NavigationHistory,
pub sort: SortSettings,
}
impl TabState {
pub fn new(cwd: PathBuf, sort: SortSettings) -> Self {
Self {
cwd,
entries: Vec::new(),
selected: 0,
history: NavigationHistory::new(),
sort,
}
}
}
#[derive(Debug)]
pub struct TabManager {
pub tabs: Vec<TabState>,
pub active: usize,
}
impl TabManager {
pub fn new(cwd: PathBuf, sort: SortSettings) -> Self {
Self {
tabs: vec![TabState::new(cwd, sort)],
active: 0,
}
}
#[allow(dead_code)]
pub fn active_tab(&self) -> &TabState {
&self.tabs[self.active]
}
pub fn active_tab_mut(&mut self) -> &mut TabState {
&mut self.tabs[self.active]
}
pub fn len(&self) -> usize {
self.tabs.len()
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.tabs.is_empty()
}
pub fn is_single(&self) -> bool {
self.tabs.len() == 1
}
pub fn open_tab(&mut self, cwd: PathBuf, sort: SortSettings) {
let insert_at = self.active + 1;
self.tabs.insert(insert_at, TabState::new(cwd, sort));
self.active = insert_at;
}
pub fn close_active_tab(&mut self) -> bool {
if self.tabs.len() == 1 {
return false;
}
self.tabs.remove(self.active);
if self.active >= self.tabs.len() {
self.active = self.tabs.len() - 1;
}
true
}
#[allow(dead_code)]
pub fn switch_to(&mut self, index: usize) -> bool {
if index >= self.tabs.len() || index == self.active {
return false;
}
self.active = index;
true
}
pub fn switch_next(&mut self) -> bool {
if self.tabs.len() <= 1 {
return false;
}
self.active = (self.active + 1) % self.tabs.len();
true
}
pub fn switch_prev(&mut self) -> bool {
if self.tabs.len() <= 1 {
return false;
}
self.active = if self.active == 0 {
self.tabs.len() - 1
} else {
self.active - 1
};
true
}
}
#[cfg(test)]
mod tests {
use super::*;
fn p(s: &str) -> PathBuf {
PathBuf::from(s)
}
#[test]
fn new_tab_manager_has_one_tab() {
let mgr = TabManager::new(p("/start"), SortSettings::default());
assert_eq!(mgr.len(), 1);
assert_eq!(mgr.active, 0);
assert_eq!(mgr.active_tab().cwd, p("/start"));
}
#[test]
fn open_tab_inserts_after_active() {
let mut mgr = TabManager::new(p("/a"), SortSettings::default());
mgr.open_tab(p("/b"), SortSettings::default());
assert_eq!(mgr.len(), 2);
assert_eq!(mgr.active, 1);
assert_eq!(mgr.active_tab().cwd, p("/b"));
}
#[test]
fn close_tab_not_allowed_when_last() {
let mut mgr = TabManager::new(p("/a"), SortSettings::default());
let closed = mgr.close_active_tab();
assert!(!closed);
assert_eq!(mgr.len(), 1);
}
#[test]
fn close_active_tab_switches_to_remaining() {
let mut mgr = TabManager::new(p("/a"), SortSettings::default());
mgr.open_tab(p("/b"), SortSettings::default());
let closed = mgr.close_active_tab();
assert!(closed);
assert_eq!(mgr.len(), 1);
assert_eq!(mgr.active, 0);
assert_eq!(mgr.active_tab().cwd, p("/a"));
}
#[test]
fn switch_next_wraps() {
let mut mgr = TabManager::new(p("/a"), SortSettings::default());
mgr.open_tab(p("/b"), SortSettings::default());
let _ = mgr.switch_to(0);
mgr.active = 0;
assert!(mgr.switch_next());
assert_eq!(mgr.active, 1);
assert!(mgr.switch_next()); assert_eq!(mgr.active, 0);
}
#[test]
fn switch_prev_wraps() {
let mut mgr = TabManager::new(p("/a"), SortSettings::default());
mgr.open_tab(p("/b"), SortSettings::default());
mgr.active = 0;
assert!(mgr.switch_prev()); assert_eq!(mgr.active, 1);
}
#[test]
fn switch_next_single_tab_returns_false() {
let mut mgr = TabManager::new(p("/a"), SortSettings::default());
assert!(!mgr.switch_next());
}
#[test]
fn switch_to_out_of_bounds_returns_false() {
let mut mgr = TabManager::new(p("/a"), SortSettings::default());
assert!(!mgr.switch_to(99));
}
#[test]
fn switch_to_same_tab_returns_false() {
let mut mgr = TabManager::new(p("/a"), SortSettings::default());
mgr.open_tab(p("/b"), SortSettings::default());
mgr.active = 1;
assert!(!mgr.switch_to(1));
}
#[test]
fn a_new_tab_inherits_the_order_it_was_opened_from() {
use crate::app::sort::SortBy;
let mut mgr = TabManager::new(p("/a"), SortSettings::default());
let inherited = SortSettings {
by: SortBy::Size,
reverse: true,
dirs_first: false,
};
mgr.open_tab(p("/b"), inherited);
assert_eq!(mgr.active_tab().sort, inherited);
assert_eq!(mgr.tabs[0].sort, SortSettings::default());
}
#[test]
fn is_single_reflects_tab_count() {
let mut mgr = TabManager::new(p("/a"), SortSettings::default());
assert!(mgr.is_single());
mgr.open_tab(p("/b"), SortSettings::default());
assert!(!mgr.is_single());
}
}