ferrite_navigation/
manager.rs

1use crate::error::{NavigationError, Result};
2use ferrite_image::SupportedFormats;
3use std::{
4    fs,
5    path::{Path, PathBuf},
6};
7use tracing::{info, instrument};
8
9pub struct NavigationManager {
10    directory_images: Vec<PathBuf>,
11    current_index:    usize,
12}
13
14impl NavigationManager {
15    pub fn new() -> Self {
16        Self {
17            directory_images: Vec::new(), current_index: 0
18        }
19    }
20
21    #[instrument(skip(self, image_path), fields(path = ?image_path))]
22    pub fn load_current_directory(&mut self, image_path: &Path) -> Result<()> {
23        let absolute_path = fs::canonicalize(image_path)
24            .map_err(NavigationError::DirectoryAccess)?;
25
26        let parent_dir = absolute_path.parent().ok_or_else(|| {
27            NavigationError::InvalidPath(image_path.to_path_buf())
28        })?;
29
30        info!("Loading images from directory: {}", parent_dir.display());
31
32        // Collect valid image paths
33        self.directory_images = fs::read_dir(parent_dir)
34            .map_err(NavigationError::DirectoryAccess)?
35            .filter_map(|entry| {
36                entry.ok().and_then(|e| {
37                    let path = e.path();
38                    if path.is_file()
39                        && SupportedFormats::is_supported(path.extension())
40                    {
41                        Some(path)
42                    } else {
43                        None
44                    }
45                })
46            })
47            .collect();
48
49        self.directory_images.sort();
50
51        self.current_index = self
52            .directory_images
53            .iter()
54            .position(|p| p == &absolute_path)
55            .unwrap_or(0);
56
57        info!(
58            "Found {} images in directory, current image at index {}",
59            self.directory_images.len(),
60            self.current_index
61        );
62
63        Ok(())
64    }
65
66    pub fn get_nearby_paths(
67        &self,
68        count: usize,
69    ) -> (Vec<PathBuf>, Vec<PathBuf>) {
70        if self.directory_images.is_empty() {
71            return (Vec::new(), Vec::new());
72        }
73
74        let total_images = self.directory_images.len();
75        let mut next_paths = Vec::with_capacity(count);
76        let mut prev_paths = Vec::with_capacity(count);
77
78        for i in 1..=count {
79            // Get next images circularly
80            let next_index = (self.current_index + i) % total_images;
81            if next_index != self.current_index {
82                next_paths.push(self.directory_images[next_index].clone());
83            }
84
85            // Get previous images circularly
86            let prev_index = if i <= self.current_index {
87                self.current_index - i
88            } else {
89                total_images - (i - self.current_index)
90            };
91            if prev_index < total_images && prev_index != self.current_index {
92                prev_paths.push(self.directory_images[prev_index].clone());
93            }
94        }
95
96        (prev_paths, next_paths)
97    }
98
99    pub fn next_image(&mut self) -> Option<PathBuf> {
100        if self.directory_images.is_empty() {
101            return None;
102        }
103        self.current_index =
104            (self.current_index + 1) % self.directory_images.len();
105        Some(self.directory_images[self.current_index].clone())
106    }
107
108    pub fn previous_image(&mut self) -> Option<PathBuf> {
109        if self.directory_images.is_empty() {
110            return None;
111        }
112        self.current_index = if self.current_index == 0 {
113            self.directory_images.len() - 1
114        } else {
115            self.current_index - 1
116        };
117        Some(self.directory_images[self.current_index].clone())
118    }
119}