use std::path::PathBuf;
use crate::core::{
ConfigCandidate, ConfigFileRule, ConfigRuleSet, ConfigTier, FilePattern, FragmentRule,
RuleBasedDiscovery, RuleMatchResult, SourceType,
};
use crate::env::StdEnv;
use super::{PathFinder, scan};
impl PathFinder {
#[must_use]
pub fn discover_with_rules(&self, rules: &ConfigRuleSet) -> RuleBasedDiscovery {
let mut main_files = Vec::new();
let mut fragments = Vec::new();
for rule in &rules.main_files {
let matches = self.find_by_rule(rule);
main_files.push(RuleMatchResult {
rule: rule.clone(),
matches,
});
}
if let Some(fragment_rule) = &rules.fragments {
fragments = self.find_fragments_by_rule(fragment_rule);
}
RuleBasedDiscovery {
rules: rules.clone(),
main_files,
fragments,
}
}
fn find_by_rule(&self, rule: &ConfigFileRule) -> Vec<ConfigCandidate> {
let mut candidates = Vec::new();
for tier in rule.tiers.tiers() {
let dirs = self.directories_for_tier(tier);
let source_type = if tier == ConfigTier::User {
SourceType::MainFile
} else {
SourceType::Legacy
};
for dir in dirs {
self.find_files_in_dirs_with_rule(
&[dir],
tier,
&rule.pattern,
&mut candidates,
source_type,
);
}
}
candidates.sort_by(|a, b| b.tier.cmp(&a.tier));
candidates
}
fn find_files_in_dirs_with_rule(
&self,
dirs: &[PathBuf],
tier: ConfigTier,
pattern: &FilePattern,
candidates: &mut Vec<ConfigCandidate>,
source_type: SourceType,
) {
candidates.extend(scan::collect_matching_candidates(
self.fs.as_ref(),
dirs,
tier,
pattern,
source_type,
|path| self.path_status(path),
));
}
fn find_fragments_by_rule(&self, rule: &FragmentRule) -> Vec<RuleMatchResult> {
let mut results = Vec::new();
let file_rule = ConfigFileRule {
pattern: rule.pattern.clone(),
tiers: rule.tiers,
required: false,
};
for tier in rule.tiers.tiers() {
let dirs = self.directories_for_tier(tier);
for base_dir in dirs {
let conf_d = base_dir.join(&rule.dir_name);
if self.fs.is_dir(&conf_d) {
let mut matches = Vec::new();
for entry in self.fs.read_dir(&conf_d) {
if rule.pattern.matches(&entry) && self.fs.is_file(&entry) {
let status = self.path_status(&entry);
matches.push(ConfigCandidate::new(
entry.clone(),
status,
tier,
SourceType::FragmentsDir,
));
}
}
if !matches.is_empty() {
results.push(RuleMatchResult {
rule: file_rule.clone(),
matches,
});
}
}
}
}
results
}
fn directories_for_tier(&self, tier: ConfigTier) -> Vec<PathBuf> {
match tier {
ConfigTier::User => self.dir_finder.user_dirs(&StdEnv),
ConfigTier::Local => self.dir_finder.local_dirs(&StdEnv),
ConfigTier::System => self.dir_finder.system_dirs(&StdEnv),
}
}
}