use std::path::PathBuf;
use std::sync::OnceLock;
use crate::search::output::style::PathDisplay;
#[derive(Debug, Clone)]
pub struct Candidate {
rel_path: PathBuf,
abs_path: PathBuf,
rel_str: OnceLock<String>,
}
impl Candidate {
#[must_use]
pub const fn new(rel_path: PathBuf, abs_path: PathBuf) -> Self {
Self {
rel_path,
abs_path,
rel_str: OnceLock::new(),
}
}
#[must_use]
pub fn rel_path(&self) -> &std::path::Path {
&self.rel_path
}
#[must_use]
pub fn abs_path(&self) -> &std::path::Path {
&self.abs_path
}
#[must_use]
pub fn rel_str(&self) -> &str {
self.rel_str
.get_or_init(|| self.rel_path.to_string_lossy().replace('\\', "/"))
}
#[must_use]
pub fn display_path(&self, display: PathDisplay, path_separator: Option<u8>) -> String {
let raw = match display {
PathDisplay::Absolute => self.abs_path().display().to_string(),
PathDisplay::Relative => self.rel_path().display().to_string(),
};
if let Some(sep) = path_separator {
let sep_char = sep as char;
raw.replace(std::path::MAIN_SEPARATOR, &sep_char.to_string())
} else {
raw
}
}
#[must_use]
pub fn within_depth(&self, max_depth: Option<usize>) -> bool {
max_depth.is_none_or(|d| self.rel_path.components().count().saturating_sub(1) <= d)
}
#[must_use]
pub fn within_filesize(&self, max_filesize: Option<u64>) -> bool {
max_filesize.is_none_or(|limit| {
std::fs::metadata(&self.abs_path).map_or(true, |m| m.len() <= limit)
})
}
#[must_use]
pub fn matches(&self, filter: &crate::search::filter::CandidateFilter) -> bool {
self.within_depth(filter.max_depth())
&& self.within_filesize(filter.max_filesize())
&& filter.matches_candidate(self)
}
}
impl PartialEq for Candidate {
fn eq(&self, other: &Self) -> bool {
self.rel_path == other.rel_path && self.abs_path == other.abs_path
}
}
impl Eq for Candidate {}