diskr 0.1.52

Lightweight terminal file explorer and disk/storage manager for macOS
#[derive(Clone, Copy)]
pub struct FooterBinding {
    pub key: &'static str,
    pub action: &'static str,
}

#[derive(Clone, Copy)]
pub struct KeyBinding {
    pub key: &'static str,
    pub action: &'static str,
    pub footer: Option<FooterBinding>,
}

pub struct KeySection {
    pub title: &'static str,
    pub bindings: &'static [KeyBinding],
}

const GLOBAL: &[KeyBinding] = &[
    KeyBinding {
        key: "?",
        action: "Show keyboard help",
        footer: Some(FooterBinding {
            key: "?",
            action: "help",
        }),
    },
    KeyBinding {
        key: "q",
        action: "Quit",
        footer: Some(FooterBinding {
            key: "q",
            action: "quit",
        }),
    },
    KeyBinding {
        key: "Esc, Ctrl+C",
        action: "Close modal or clear search/filter",
        footer: None,
    },
];

const NAVIGATION: &[KeyBinding] = &[
    KeyBinding {
        key: "Up/Down, j/k",
        action: "Move selection",
        footer: Some(FooterBinding {
            key: "↑↓/jk",
            action: "move",
        }),
    },
    KeyBinding {
        key: "PageUp/PageDown",
        action: "Move one page",
        footer: None,
    },
    KeyBinding {
        key: "Home/End",
        action: "Jump first/last",
        footer: None,
    },
    KeyBinding {
        key: "Enter",
        action: "Open selected item",
        footer: Some(FooterBinding {
            key: "",
            action: "open",
        }),
    },
    KeyBinding {
        key: "Backspace",
        action: "Go to parent directory",
        footer: Some(FooterBinding {
            key: "",
            action: "up",
        }),
    },
    KeyBinding {
        key: "Left/Right, h/l",
        action: "Switch pane or package view",
        footer: Some(FooterBinding {
            key: "←→/hl",
            action: "pane/view",
        }),
    },
    KeyBinding {
        key: "Tab, Shift+Tab",
        action: "Cycle panes",
        footer: Some(FooterBinding {
            key: "Tab",
            action: "pane",
        }),
    },
];

const FILES: &[KeyBinding] = &[
    KeyBinding {
        key: "/",
        action: "Search files or filter packages; Enter keeps, Esc clears",
        footer: Some(FooterBinding {
            key: "/",
            action: "search",
        }),
    },
    KeyBinding {
        key: "i",
        action: "Show details",
        footer: Some(FooterBinding {
            key: "i",
            action: "info",
        }),
    },
    KeyBinding {
        key: ".",
        action: "Toggle hidden files",
        footer: None,
    },
    KeyBinding {
        key: "o",
        action: "Cycle sort mode",
        footer: None,
    },
    KeyBinding {
        key: "r",
        action: "Refresh view and rescan visible dirs",
        footer: Some(FooterBinding {
            key: "r",
            action: "refresh",
        }),
    },
    KeyBinding {
        key: "S",
        action: "Scan missing or stale visible directory sizes",
        footer: Some(FooterBinding {
            key: "S",
            action: "scan all",
        }),
    },
    KeyBinding {
        key: "B",
        action: "Save history baseline",
        footer: None,
    },
    KeyBinding {
        key: "t",
        action: "Open top-files list",
        footer: None,
    },
    KeyBinding {
        key: "c",
        action: "Rename selected file",
        footer: None,
    },
    KeyBinding {
        key: "n",
        action: "Create directory",
        footer: None,
    },
    KeyBinding {
        key: "v",
        action: "Toggle mark",
        footer: None,
    },
    KeyBinding {
        key: "a",
        action: "Mark all visible files",
        footer: None,
    },
    KeyBinding {
        key: "d",
        action: "Move selected item to Trash",
        footer: None,
    },
];

const EXTERNAL_ACTIONS: &[KeyBinding] = &[
    KeyBinding {
        key: "Space",
        action: "Quick Look selected item",
        footer: Some(FooterBinding {
            key: "Space",
            action: "preview",
        }),
    },
    KeyBinding {
        key: "f",
        action: "Reveal selected item in Finder",
        footer: None,
    },
    KeyBinding {
        key: "O",
        action: "Open selected item with default app",
        footer: None,
    },
    KeyBinding {
        key: "y",
        action: "Copy selected path",
        footer: None,
    },
    KeyBinding {
        key: "s",
        action: "Open selected location in Terminal",
        footer: None,
    },
];

const PACKAGES: &[KeyBinding] = &[
    KeyBinding {
        key: "p",
        action: "Open packages pane / switch view",
        footer: Some(FooterBinding {
            key: "p",
            action: "packages",
        }),
    },
    KeyBinding {
        key: "u",
        action: "Toggle dependency-leaf filter",
        footer: None,
    },
    KeyBinding {
        key: "x",
        action: "Uninstall selected package",
        footer: None,
    },
];

const RECLAIM_REPORTS: &[KeyBinding] = &[
    KeyBinding {
        key: "R",
        action: "Re-scan reclaim pane",
        footer: None,
    },
    KeyBinding {
        key: "E",
        action: "Empty Trash (when the report lists Trash)",
        footer: None,
    },
    KeyBinding {
        key: "d",
        action: "Trash selected reclaim/top-files path",
        footer: None,
    },
];

pub const HELP_SECTIONS: &[KeySection] = &[
    KeySection {
        title: "Global",
        bindings: GLOBAL,
    },
    KeySection {
        title: "Navigation",
        bindings: NAVIGATION,
    },
    KeySection {
        title: "Files",
        bindings: FILES,
    },
    KeySection {
        title: "Actions",
        bindings: EXTERNAL_ACTIONS,
    },
    KeySection {
        title: "Packages",
        bindings: PACKAGES,
    },
    KeySection {
        title: "Reclaim / Reports",
        bindings: RECLAIM_REPORTS,
    },
];

pub fn footer_bindings() -> impl Iterator<Item = FooterBinding> {
    HELP_SECTIONS
        .iter()
        .flat_map(|section| section.bindings.iter())
        .filter_map(|binding| binding.footer)
}

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

    #[test]
    fn keymap_includes_help_binding() {
        assert!(HELP_SECTIONS
            .iter()
            .flat_map(|section| section.bindings.iter())
            .any(|binding| binding.key == "?"));
    }

    #[test]
    fn footer_advertises_help_binding() {
        assert!(footer_bindings().any(|binding| binding.key == "?"));
    }
}