use crate::abc::Limits;
use crate::cache;
use crate::git_changes;
use crate::paths::{Lang, lang_for, parse_file_lang};
use crate::srcbuf::{SrcBuf, load_src};
mod backends;
mod narrow;
mod non_clike;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod tests_fs;
#[derive(Debug)]
pub(crate) struct Checks {
pub want_abc: bool,
pub want_used: bool,
pub want_never: bool,
}
impl Checks {
pub fn new(only: Option<&str>) -> Self {
Self {
want_abc: only.is_none_or(|o| o == "abc"),
want_used: only.is_none_or(|o| o == "used-once"),
want_never: only.is_none_or(|o| o == "never-used"),
}
}
}
fn blank_with(path: &std::path::Path) -> crate::output::FileResult {
crate::output::FileResult {
path: path.display().to_string(),
abc: Vec::new(),
used_once: Vec::new(),
never_used: Vec::new(),
module_abc: None,
}
}
fn reparsed<'a>(src_bytes: &'a [u8], lang: Lang) -> Option<crate::model::FileModel<'a>> {
let tree = parse_file_lang(src_bytes, lang)?;
Some(crate::model::build(src_bytes, tree))
}
pub(crate) fn analyze_one(
path: &std::path::Path,
only: Option<&str>,
limits: Limits,
changeset: Option<&git_changes::Changeset>,
cache: Option<&cache::Cache>,
) -> crate::output::FileResult {
let Some((mut r, src_buf)) = loaded_result(path) else {
return blank_with(path);
};
let hit = cache.and_then(|c| cache_hit(c, path, &src_buf, only, limits));
if let Some(mut hit) = hit {
narrow::apply(changeset, &mut hit, limits.module, &src_buf);
return hit;
}
let file_lang = lang_for(path);
let Some(tree) = parse_file_lang(&src_buf, file_lang) else {
return r;
};
let checks = Checks::new(only);
if file_lang.is_clike() {
backends::clike_arm(&mut r, file_lang, &src_buf, &tree, &checks, limits);
} else if !backends::non_clike_arm(&mut r, file_lang, &src_buf, tree, &checks, limits) {
return r;
}
store_result(cache, path, &src_buf, only, limits, &r);
narrow::apply(changeset, &mut r, limits.module, &src_buf);
r
}
fn loaded_result(path: &std::path::Path) -> Option<(crate::output::FileResult, SrcBuf)> {
let r = blank_with(path);
let src_buf = load_src(path).ok()?;
Some((r, src_buf))
}
fn cache_hit(
cache: &cache::Cache,
path: &std::path::Path,
src: &[u8],
only: Option<&str>,
limits: Limits,
) -> Option<crate::output::FileResult> {
let (abc, used_once, never_used, module_abc) =
cache.get(&cache.file_key(path, src, only, limits))?;
Some(crate::output::FileResult {
path: path.display().to_string(),
abc,
used_once,
never_used,
module_abc,
})
}
fn store_result(
cache: Option<&cache::Cache>,
path: &std::path::Path,
src: &[u8],
only: Option<&str>,
limits: Limits,
r: &crate::output::FileResult,
) {
if let Some(cache) = cache {
cache.store(
&cache.file_key(path, src, only, limits),
&r.abc,
&r.used_once,
&r.never_used,
r.module_abc.clone(),
);
}
}