fast-fs 0.2.1

High-speed async file system traversal library with batteries-included file browser component
Documentation
// <FILE>crates/fast-fs/src/nav/browser_config.rs</FILE> - <DESC>Browser configuration</DESC>
// <VERS>VERSION: 0.1.0</VERS>
// <WCTX>Implementing nav module PRD</WCTX>
// <CLOG>Initial creation with presets for open/save dialogs</CLOG>

//! Browser configuration
//!
//! Configuration options for the Browser, including key bindings,
//! initial state, and behavior toggles.

use super::cls_key_map::KeyMap;
use crate::SortBy;
use std::path::PathBuf;

/// Configuration for the Browser
///
/// All fields have sensible defaults. Use the builder pattern or
/// struct update syntax to customize.
///
/// # Examples
///
/// ```
/// use fast_fs::nav::BrowserConfig;
///
/// // Use presets for common scenarios
/// let open_config = BrowserConfig::open_dialog();
/// let save_config = BrowserConfig::save_dialog();
/// let explorer_config = BrowserConfig::project_explorer();
///
/// // Or customize from default
/// let custom = BrowserConfig {
///     show_hidden: true,
///     scroll_padding: 3,
///     ..Default::default()
/// };
/// ```
#[derive(Debug, Clone)]
pub struct BrowserConfig {
    /// Key-to-action bindings
    pub keymap: KeyMap,

    /// Initial directory path (None = current directory)
    pub initial_path: Option<PathBuf>,

    /// Show hidden files by default
    pub show_hidden: bool,

    /// Default sort order
    pub sort_by: SortBy,

    /// Lines to keep visible above/below cursor when scrolling
    pub scroll_padding: usize,

    /// Maximum history entries to keep
    pub history_limit: usize,

    /// Clear selection when navigating to a new directory
    pub clear_selection_on_navigate: bool,

    /// Confirm before deleting files
    pub confirm_delete: bool,

    /// Confirm before overwriting files
    pub confirm_overwrite: bool,

    /// Readonly mode — disables all file mutations.
    /// Immutable after construction. To toggle, create a new Browser.
    pub readonly: bool,

    /// Apply ignore rules when loading directories.
    /// Respects both .gitignore and .ignore files (in that priority order).
    pub respect_ignore_files: bool,

    /// Show ".." entry at top of directories (for visual parent navigation).
    /// When true, directories with parents show ".." as first entry.
    /// Selecting ".." is equivalent to GoParent action.
    pub show_parent_entry: bool,
}

impl Default for BrowserConfig {
    fn default() -> Self {
        Self {
            keymap: KeyMap::default(),
            initial_path: None,
            show_hidden: false,
            sort_by: SortBy::Name,
            scroll_padding: 2,
            history_limit: 50,
            clear_selection_on_navigate: true,
            confirm_delete: true,
            confirm_overwrite: true,
            readonly: false,
            respect_ignore_files: false,
            show_parent_entry: true,
        }
    }
}

impl BrowserConfig {
    /// Config for Open File dialog (readonly, safe)
    ///
    /// Use this for file pickers where users should only be able to
    /// navigate and select files, not modify them.
    pub fn open_dialog() -> Self {
        Self {
            readonly: true,
            ..Default::default()
        }
    }

    /// Config for Save File dialog (writable)
    ///
    /// Use this for save dialogs where users may need to create
    /// directories or overwrite existing files.
    pub fn save_dialog() -> Self {
        Self {
            readonly: false,
            ..Default::default()
        }
    }

    /// Config for project explorer (respects .gitignore and .ignore)
    ///
    /// Use this for IDE-style project explorers that should hide
    /// files matching ignore patterns.
    pub fn project_explorer() -> Self {
        Self {
            respect_ignore_files: true,
            show_hidden: false,
            ..Default::default()
        }
    }

    /// Builder method: set initial path
    pub fn with_initial_path(mut self, path: impl Into<PathBuf>) -> Self {
        self.initial_path = Some(path.into());
        self
    }

    /// Builder method: set keymap
    pub fn with_keymap(mut self, keymap: KeyMap) -> Self {
        self.keymap = keymap;
        self
    }

    /// Builder method: set show_hidden
    pub fn with_show_hidden(mut self, show: bool) -> Self {
        self.show_hidden = show;
        self
    }

    /// Builder method: set sort_by
    pub fn with_sort_by(mut self, sort_by: SortBy) -> Self {
        self.sort_by = sort_by;
        self
    }

    /// Builder method: set readonly mode
    pub fn with_readonly(mut self, readonly: bool) -> Self {
        self.readonly = readonly;
        self
    }

    /// Builder method: set respect_ignore_files
    pub fn with_respect_ignore_files(mut self, respect: bool) -> Self {
        self.respect_ignore_files = respect;
        self
    }

    /// Builder method: set show_parent_entry
    pub fn with_show_parent_entry(mut self, show: bool) -> Self {
        self.show_parent_entry = show;
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_default() {
        let config = BrowserConfig::default();
        assert!(!config.readonly);
        assert!(!config.show_hidden);
        assert!(config.show_parent_entry);
        assert!(config.confirm_delete);
    }

    #[test]
    fn test_open_dialog_is_readonly() {
        let config = BrowserConfig::open_dialog();
        assert!(config.readonly);
    }

    #[test]
    fn test_save_dialog_is_writable() {
        let config = BrowserConfig::save_dialog();
        assert!(!config.readonly);
    }

    #[test]
    fn test_project_explorer_respects_ignore() {
        let config = BrowserConfig::project_explorer();
        assert!(config.respect_ignore_files);
        assert!(!config.show_hidden);
    }

    #[test]
    fn test_builder_pattern() {
        let config = BrowserConfig::default()
            .with_show_hidden(true)
            .with_readonly(true)
            .with_initial_path("/home/user");

        assert!(config.show_hidden);
        assert!(config.readonly);
        assert_eq!(config.initial_path, Some(PathBuf::from("/home/user")));
    }
}

// <FILE>crates/fast-fs/src/nav/browser_config.rs</FILE>
// <VERS>END OF VERSION: 0.1.0</VERS>