use tree_sitter::Tree;
use crate::abc::{AbcOffense, Limits};
use crate::directives::{self, Directives};
use crate::modulesize;
use crate::never_used::NeverUsedOffense;
use crate::output::FileResult;
use crate::used_once::UsedOnceOffense;
use super::Checks;
use super::non_clike::{CSharpB, DartB, GoB, JavaB, NonClike, PhpB, PyB, SolidityB, ZigB};
use crate::paths::Lang;
fn directives_for(src: &[u8]) -> Directives {
directives::parse(&String::from_utf8_lossy(src))
}
fn suppressed<T>(v: Vec<T>, keep: impl Fn(&T) -> bool) -> Vec<T> {
v.into_iter().filter(|o| !keep(o)).collect()
}
fn set_module_abc(r: &mut FileResult, src: &[u8], all: &[AbcOffense], max: f64) {
let text = std::str::from_utf8(src).unwrap_or("");
r.module_abc = modulesize::from_scores(all, &r.path, text, max);
}
fn keep_abc(dirs: &Directives, all: Vec<AbcOffense>, max: f64) -> Vec<AbcOffense> {
suppressed(
all.into_iter().filter(|o| o.score > max).collect(),
|o| dirs.suppresses_abc(o.line),
)
}
pub(super) fn clike_arm(
r: &mut FileResult,
lang: Lang,
src: &[u8],
tree: &Tree,
checks: &Checks,
limits: Limits,
) {
let dirs = directives_for(src);
let all = crate::clike::all_scores(src, tree, lang);
set_module_abc(r, src, &all, limits.module);
if checks.want_abc {
r.abc = keep_abc(&dirs, all, limits.method);
}
if !matches!(
lang,
Lang::Js | Lang::Ts | Lang::Tsx | Lang::Swift | Lang::C | Lang::Cpp | Lang::ObjC
) {
return;
}
let scopes = crate::clike::collect_scopes(src, tree, lang);
if checks.want_used {
r.used_once = suppressed(crate::clike::used_once_offenses(&scopes, lang), |o| {
dirs.suppresses_all(o.line)
});
}
if checks.want_never {
r.never_used = crate::clike::never_used_offenses(&scopes, lang);
}
}
fn directed<A, U, N>(
r: &mut FileResult,
src: &[u8],
dirs: &Directives,
checks: &Checks,
limits: Limits,
all_scores: A,
used_once: U,
never_used: N,
) where
A: FnOnce() -> Vec<AbcOffense>,
U: FnOnce() -> Vec<UsedOnceOffense>,
N: FnOnce() -> Vec<NeverUsedOffense>,
{
let all = all_scores();
set_module_abc(r, src, &all, limits.module);
if checks.want_abc {
r.abc = keep_abc(dirs, all, limits.method);
}
if checks.want_used {
r.used_once = suppressed(used_once(), |o| dirs.suppresses_all(o.line));
}
if checks.want_never {
r.never_used = never_used();
}
}
fn non_clike_directed<B: NonClike>(
r: &mut FileResult,
src: &[u8],
tree: Tree,
checks: &Checks,
limits: Limits,
) -> bool {
let fm = B::build(src, tree);
directed(
r,
src,
&directives_for(src),
checks,
limits,
|| B::all_scores(&fm),
|| B::used_once_offenses(&fm),
|| B::never_used_offenses(&fm),
);
true
}
fn rust_arm(
r: &mut FileResult,
src: &[u8],
tree: Tree,
checks: &Checks,
limits: Limits,
) -> bool {
let fm = crate::rustlang::build(src, tree);
let all = crate::rustlang::all_scores(&fm);
set_module_abc(r, src, &all, limits.module);
if checks.want_abc {
r.abc = all
.into_iter()
.filter(|o| o.score > limits.method)
.collect();
}
if checks.want_used {
r.used_once = crate::rustlang::used_once_offenses(&fm);
}
if checks.want_never {
r.never_used = crate::rustlang::never_used_offenses(&fm);
}
true
}
fn ruby_arm(r: &mut FileResult, src: &[u8], checks: &Checks, limits: Limits) -> bool {
let dirs = directives_for(src);
let Some(fm) = super::reparsed(src, Lang::Ruby) else {
return false;
};
let all = crate::abc::all_scores(&fm);
set_module_abc(r, src, &all, limits.module);
if checks.want_abc {
r.abc = keep_abc(&dirs, all, limits.method);
}
if checks.want_used {
r.used_once = suppressed(crate::used_once::analyze(&fm), |o| {
dirs.suppresses_all(o.line)
});
}
if checks.want_never {
r.never_used = crate::never_used::analyze(&fm);
}
true
}
pub(super) fn non_clike_arm(
r: &mut FileResult,
lang: Lang,
src: &[u8],
tree: Tree,
checks: &Checks,
limits: Limits,
) -> bool {
match lang {
Lang::Rust => rust_arm(r, src, tree, checks, limits),
Lang::Ruby => ruby_arm(r, src, checks, limits),
Lang::Py => non_clike_directed::<PyB>(r, src, tree, checks, limits),
Lang::Go => non_clike_directed::<GoB>(r, src, tree, checks, limits),
Lang::Php => non_clike_directed::<PhpB>(r, src, tree, checks, limits),
Lang::Java => non_clike_directed::<JavaB>(r, src, tree, checks, limits),
Lang::CSharp => non_clike_directed::<CSharpB>(r, src, tree, checks, limits),
Lang::Solidity => non_clike_directed::<SolidityB>(r, src, tree, checks, limits),
Lang::Dart => non_clike_directed::<DartB>(r, src, tree, checks, limits),
Lang::Zig => non_clike_directed::<ZigB>(r, src, tree, checks, limits),
_ => unreachable!("unsupported non-clike language"),
}
}