spreadsheet_mcp/tools/
filters.rs

1use anyhow::{Result, anyhow};
2use globset::{Glob, GlobMatcher};
3use std::path::Path;
4
5#[derive(Default)]
6pub struct WorkbookFilter {
7    slug_prefix: Option<String>,
8    folder: Option<String>,
9    path_glob: Option<GlobMatcher>,
10}
11
12impl WorkbookFilter {
13    pub fn new(
14        slug_prefix: Option<String>,
15        folder: Option<String>,
16        path_glob: Option<String>,
17    ) -> Result<Self> {
18        let matcher = if let Some(glob) = path_glob {
19            Some(
20                Glob::new(&glob)
21                    .map_err(|err| anyhow!("invalid glob pattern {glob}: {err}"))?
22                    .compile_matcher(),
23            )
24        } else {
25            None
26        };
27
28        Ok(Self {
29            slug_prefix: slug_prefix.map(|s| s.to_ascii_lowercase()),
30            folder: folder.map(|s| s.to_ascii_lowercase()),
31            path_glob: matcher,
32        })
33    }
34
35    pub fn matches(&self, slug: &str, folder: Option<&str>, path: &Path) -> bool {
36        if let Some(prefix) = &self.slug_prefix
37            && !slug.to_ascii_lowercase().starts_with(prefix)
38        {
39            return false;
40        }
41
42        if let Some(expected_folder) = &self.folder {
43            match folder.map(|f| f.to_ascii_lowercase()) {
44                Some(actual) if &actual == expected_folder => {}
45                _ => return false,
46            }
47        }
48
49        if let Some(glob) = &self.path_glob
50            && !glob.is_match(path)
51        {
52            return false;
53        }
54
55        true
56    }
57}