spreadsheet_mcp/tools/
filters.rs1use 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}