1use std::path::Path;
2
3use anyhow::Context as _;
4use settings::Settings;
5use util::{
6 ResultExt,
7 paths::{PathMatcher, PathStyle},
8 rel_path::RelPath,
9};
10
11#[derive(Clone, PartialEq, Eq)]
12pub struct WorktreeSettings {
13 pub project_name: Option<String>,
14 /// Whether to prevent this project from being shared in public channels.
15 pub prevent_sharing_in_public_channels: bool,
16 pub file_scan_exclusions: PathMatcher,
17 pub file_scan_inclusions: PathMatcher,
18 /// This field contains all ancestors of the `file_scan_inclusions`. It's used to
19 /// determine whether to terminate worktree scanning for a given dir.
20 pub parent_dir_scan_inclusions: PathMatcher,
21 pub private_files: PathMatcher,
22}
23
24impl WorktreeSettings {
25 pub fn is_path_private(&self, path: &RelPath) -> bool {
26 path.ancestors()
27 .any(|ancestor| self.private_files.is_match(ancestor.as_std_path()))
28 }
29
30 pub fn is_path_excluded(&self, path: &RelPath) -> bool {
31 path.ancestors()
32 .any(|ancestor| self.file_scan_exclusions.is_match(ancestor.as_std_path()))
33 }
34
35 pub fn is_path_always_included(&self, path: &RelPath, is_dir: bool) -> bool {
36 if is_dir {
37 self.parent_dir_scan_inclusions.is_match(path.as_std_path())
38 } else {
39 self.file_scan_inclusions.is_match(path.as_std_path())
40 }
41 }
42}
43
44impl Settings for WorktreeSettings {
45 fn from_settings(content: &settings::SettingsContent) -> Self {
46 let worktree = content.project.worktree.clone();
47 let file_scan_exclusions = worktree.file_scan_exclusions.unwrap();
48 let file_scan_inclusions = worktree.file_scan_inclusions.unwrap();
49 let private_files = worktree.private_files.unwrap().0;
50 let parsed_file_scan_inclusions: Vec<String> = file_scan_inclusions
51 .iter()
52 .flat_map(|glob| {
53 Path::new(glob)
54 .ancestors()
55 .skip(1)
56 .map(|a| a.to_string_lossy().into())
57 })
58 .filter(|p: &String| !p.is_empty())
59 .collect();
60
61 Self {
62 project_name: worktree.project_name.into_inner(),
63 prevent_sharing_in_public_channels: worktree.prevent_sharing_in_public_channels,
64 file_scan_exclusions: path_matchers(file_scan_exclusions, "file_scan_exclusions")
65 .log_err()
66 .unwrap_or_default(),
67 parent_dir_scan_inclusions: path_matchers(
68 parsed_file_scan_inclusions,
69 "file_scan_inclusions",
70 )
71 .unwrap(),
72 file_scan_inclusions: path_matchers(file_scan_inclusions, "file_scan_inclusions")
73 .unwrap(),
74 private_files: path_matchers(private_files, "private_files")
75 .log_err()
76 .unwrap_or_default(),
77 }
78 }
79}
80
81fn path_matchers(mut values: Vec<String>, context: &'static str) -> anyhow::Result<PathMatcher> {
82 values.sort();
83 PathMatcher::new(values, PathStyle::local())
84 .with_context(|| format!("Failed to parse globs from {}", context))
85}