cfgmatic_paths/builder/
rules.rs1use std::path::PathBuf;
4
5use crate::core::{
6 ConfigCandidate, ConfigFileRule, ConfigRuleSet, ConfigTier, FilePattern, FragmentRule,
7 RuleBasedDiscovery, RuleMatchResult, SourceType,
8};
9use crate::env::StdEnv;
10
11use super::{PathFinder, scan};
12
13impl PathFinder {
14 #[must_use]
43 pub fn discover_with_rules(&self, rules: &ConfigRuleSet) -> RuleBasedDiscovery {
44 let mut main_files = Vec::new();
45 let mut fragments = Vec::new();
46
47 for rule in &rules.main_files {
48 let matches = self.find_by_rule(rule);
49 main_files.push(RuleMatchResult {
50 rule: rule.clone(),
51 matches,
52 });
53 }
54
55 if let Some(fragment_rule) = &rules.fragments {
56 fragments = self.find_fragments_by_rule(fragment_rule);
57 }
58
59 RuleBasedDiscovery {
60 rules: rules.clone(),
61 main_files,
62 fragments,
63 }
64 }
65
66 fn find_by_rule(&self, rule: &ConfigFileRule) -> Vec<ConfigCandidate> {
68 let mut candidates = Vec::new();
69
70 for tier in rule.tiers.tiers() {
71 let dirs = self.directories_for_tier(tier);
72 let source_type = if tier == ConfigTier::User {
73 SourceType::MainFile
74 } else {
75 SourceType::Legacy
76 };
77
78 for dir in dirs {
79 self.find_files_in_dirs_with_rule(
80 &[dir],
81 tier,
82 &rule.pattern,
83 &mut candidates,
84 source_type,
85 );
86 }
87 }
88
89 candidates.sort_by(|a, b| b.tier.cmp(&a.tier));
90 candidates
91 }
92
93 fn find_files_in_dirs_with_rule(
95 &self,
96 dirs: &[PathBuf],
97 tier: ConfigTier,
98 pattern: &FilePattern,
99 candidates: &mut Vec<ConfigCandidate>,
100 source_type: SourceType,
101 ) {
102 candidates.extend(scan::collect_matching_candidates(
103 self.fs.as_ref(),
104 dirs,
105 tier,
106 pattern,
107 source_type,
108 |path| self.path_status(path),
109 ));
110 }
111
112 fn find_fragments_by_rule(&self, rule: &FragmentRule) -> Vec<RuleMatchResult> {
114 let mut results = Vec::new();
115 let file_rule = ConfigFileRule {
116 pattern: rule.pattern.clone(),
117 tiers: rule.tiers,
118 required: false,
119 };
120
121 for tier in rule.tiers.tiers() {
122 let dirs = self.directories_for_tier(tier);
123
124 for base_dir in dirs {
125 let conf_d = base_dir.join(&rule.dir_name);
126 if self.fs.is_dir(&conf_d) {
127 let mut matches = Vec::new();
128
129 for entry in self.fs.read_dir(&conf_d) {
130 if rule.pattern.matches(&entry) && self.fs.is_file(&entry) {
131 let status = self.path_status(&entry);
132 matches.push(ConfigCandidate::new(
133 entry.clone(),
134 status,
135 tier,
136 SourceType::FragmentsDir,
137 ));
138 }
139 }
140
141 if !matches.is_empty() {
142 results.push(RuleMatchResult {
143 rule: file_rule.clone(),
144 matches,
145 });
146 }
147 }
148 }
149 }
150
151 results
152 }
153
154 fn directories_for_tier(&self, tier: ConfigTier) -> Vec<PathBuf> {
156 match tier {
157 ConfigTier::User => self.dir_finder.user_dirs(&StdEnv),
158 ConfigTier::Local => self.dir_finder.local_dirs(&StdEnv),
159 ConfigTier::System => self.dir_finder.system_dirs(&StdEnv),
160 }
161 }
162}