use std::{fmt::Debug, path::Path};
use super::filter::FilterExpr;
use crate::{
file_util::PathPair,
paths_selector::PathsSelector,
result::{ignore_error, trace_ok_err},
sort_params::{SortParams, SortType},
tools_data::ToolsDataMap,
};
use exmex::prelude::*;
use rvimage_domain::RvResult;
fn next(file_selected_idx: usize, files_len: usize) -> usize {
if file_selected_idx < files_len - 1 {
file_selected_idx + 1
} else {
files_len - 1
}
}
fn prev(file_selected_idx: usize, files_len: usize) -> usize {
if file_selected_idx >= files_len {
files_len - 1
} else if file_selected_idx > 0 {
file_selected_idx - 1
} else {
0
}
}
#[derive(Default)]
pub struct PathsNavigator {
file_label_selected_idx: Option<usize>,
paths_selector: Option<PathsSelector>,
scroll_to_selected_label: bool,
}
impl PathsNavigator {
pub fn new(
mut paths_selector: Option<PathsSelector>,
sort_params: SortParams,
) -> RvResult<Self> {
if let Some(ps) = &mut paths_selector {
match sort_params.kind {
SortType::Natural => ps.natural_sort(sort_params.sort_by_filename)?,
SortType::Alphabetical => ps.alphabetical_sort(sort_params.sort_by_filename)?,
}
};
Ok(Self {
file_label_selected_idx: None,
paths_selector,
scroll_to_selected_label: false,
})
}
fn pn(&mut self, f: fn(usize, usize) -> usize) {
if let Some(idx) = self.file_label_selected_idx
&& let Some(ps) = &self.paths_selector
{
self.file_label_selected_idx = Some(f(idx, ps.len_filtered()));
self.scroll_to_selected_label = true;
}
}
pub fn next(&mut self) {
self.pn(next);
}
pub fn prev(&mut self) {
self.pn(prev);
}
pub fn file_label_selected_idx(&self) -> Option<usize> {
self.file_label_selected_idx
}
pub fn len_filtered(&self) -> Option<usize> {
self.paths_selector.as_ref().map(|ps| ps.len_filtered())
}
pub fn scroll_to_selected_label(&self) -> bool {
self.scroll_to_selected_label
}
pub fn activate_scroll_to_selected_label(&mut self) {
self.scroll_to_selected_label = true;
}
pub fn deactivate_scroll_to_selected_label(&mut self) {
self.scroll_to_selected_label = false;
}
pub fn select_label_idx(&mut self, filtered_label_idx: Option<usize>) {
if let (Some(idx), Some(ps)) = (filtered_label_idx, self.paths_selector())
&& idx < ps.len_filtered()
{
self.file_label_selected_idx = Some(idx);
}
}
pub fn export_filtered_filelist<P>(&self, export_path: P, filter_str: &str) -> RvResult<()>
where
P: AsRef<Path> + Debug,
{
if let Some(ps) = self.paths_selector() {
ps.export_filtered(export_path, filter_str)?
};
Ok(())
}
fn idx_of_file_label(&self, file_label: &str) -> Option<usize> {
match self.paths_selector() {
Some(ps) => ps.filteredidx_of_file_label(file_label),
None => None,
}
}
pub fn select_file_label(&mut self, file_label: &str) {
self.select_label_idx(self.idx_of_file_label(file_label));
}
pub fn paths_selector(&self) -> Option<&PathsSelector> {
self.paths_selector.as_ref()
}
fn filter_by_pred(&mut self, filter_predicate: impl FnMut(&str) -> bool) -> RvResult<()> {
if let Some(ps) = &mut self.paths_selector {
let unfiltered_idx_before_filter =
if let Some(filtered_idx) = self.file_label_selected_idx {
self.scroll_to_selected_label = true;
ps.filtered_idx_file_label_pairs(filtered_idx)
.map(|ffl| ffl.idx)
} else {
None
};
ps.filter(filter_predicate)?;
self.file_label_selected_idx = match unfiltered_idx_before_filter {
Some(unfiltered_idx) => ps
.filtered_iter_idx()
.enumerate()
.find(|(_, uidx)| *uidx == unfiltered_idx)
.map(|(fidx, _)| fidx),
None => None,
};
}
Ok(())
}
pub fn filter(
&mut self,
s: &str,
tools_data_map: &ToolsDataMap,
active_tool_name: Option<&str>,
) -> RvResult<()> {
if let Some(filter_pred) =
ignore_error(FilterExpr::parse(s).and_then(|expr| expr.eval(&[])))
{
let filter_pred_wrapper = |path: &str| {
trace_ok_err(filter_pred.apply(path, Some(tools_data_map), active_tool_name))
.unwrap_or(true)
};
self.filter_by_pred(filter_pred_wrapper)?;
} else {
let trimmed = s.trim();
let filter_pred = |path: &str| {
if path.is_empty() {
true
} else {
path.contains(trimmed)
}
};
self.filter_by_pred(filter_pred)?;
}
Ok(())
}
pub fn folder_label(&self) -> Option<&str> {
self.paths_selector().as_ref().map(|ps| ps.folder_label())
}
pub fn file_path(&self, file_idx: usize) -> Option<&PathPair> {
self.paths_selector()
.as_ref()
.and_then(|ps| ps.file_selected_path(file_idx))
}
}
#[test]
fn test_prev_next() {
assert_eq!(next(3, 4), 3);
assert_eq!(next(2, 4), 3);
assert_eq!(next(5, 4), 3);
assert_eq!(next(1, 4), 2);
assert_eq!(prev(3, 4), 2);
assert_eq!(prev(2, 3), 1);
assert_eq!(prev(3, 3), 2);
assert_eq!(prev(4, 3), 2);
assert_eq!(prev(9, 3), 2);
}