fm/modes/menu/
filter.rs

1use std::{borrow::Borrow, fmt::Display};
2
3use regex::Regex;
4
5use crate::modes::{FileInfo, FileKind};
6
7/// Different kinds of filters.
8/// By extension, by name, only the directory or all files.
9#[derive(Clone)]
10pub enum FilterKind {
11    Extension(String),
12    Name(String),
13    Directory,
14    All,
15}
16
17impl FilterKind {
18    /// Parse the input string into a filter.
19    /// It shouldn't fail but use a `Filter::All` if the string isn't parsable;
20    #[must_use]
21    pub fn from_input(input: &str) -> Self {
22        let words: Vec<&str> = input.split_whitespace().collect();
23        match words.first() {
24            Some(&"d") => Self::Directory,
25            Some(&"e") if words.len() > 1 => Self::Extension(words[1].to_owned()),
26            Some(&"n") if words.len() > 1 => Self::Name(words[1].to_owned()),
27            _ => Self::All,
28        }
29    }
30
31    /// Apply the selected filter to the file list.
32    /// It's a "key" used by the Filter method to hold the files matching this
33    /// filter.
34    #[must_use]
35    pub fn filter_by(&self, fileinfo: &FileInfo, keep_dirs: bool) -> bool {
36        match self {
37            Self::Extension(ext) => Self::filter_by_extension(fileinfo, ext, keep_dirs),
38            Self::Name(filename) => Self::filter_by_name(fileinfo, filename, keep_dirs),
39            Self::Directory => Self::filter_directory(fileinfo),
40            Self::All => true,
41        }
42    }
43
44    fn filter_by_extension(fileinfo: &FileInfo, ext: &str, keep_dirs: bool) -> bool {
45        let file_ext: &str = fileinfo.extension.borrow();
46        file_ext == ext || keep_dirs && fileinfo.is_dir()
47    }
48
49    fn filter_by_name(fileinfo: &FileInfo, filename: &str, keep_dirs: bool) -> bool {
50        if keep_dirs && fileinfo.is_dir() {
51            true
52        } else {
53            let Ok(re) = Regex::new(filename) else {
54                return false;
55            };
56            re.is_match(&fileinfo.filename)
57        }
58    }
59
60    fn filter_directory(fileinfo: &FileInfo) -> bool {
61        matches!(fileinfo.file_kind, FileKind::Directory)
62    }
63}
64
65/// Format the corresponding variant to be printed in the second line.
66/// We display a simple line with a variant description and the typed filter (if any).
67impl Display for FilterKind {
68    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
69        match self {
70            Self::Directory => write!(f, "Filter: Directory only"),
71            Self::Extension(s) => write!(f, "Filter: by extension \"{s}\""),
72            Self::Name(s) => write!(f, "Filter: by name \"{s}\""),
73            Self::All => write!(f, ""),
74        }
75    }
76}