#![allow(
clippy::enum_glob_use,
clippy::too_many_lines,
clippy::wildcard_imports
)]
#![allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
use super::{
Abc, Stats, count_boolean_slot, count_each_operand, count_negated_operand, wrapped_operand,
};
use crate::*;
fn irules_ternary_slots<'a>(
node: &Node<'a>,
) -> (Option<Node<'a>>, Option<Node<'a>>, Option<Node<'a>>) {
let (mut condition, mut consequence, mut alternative) = (None, None, None);
let mut seen_question = false;
let mut seen_colon = false;
for child in node.children() {
match child.kind_id().into() {
Irules::QMARK => seen_question = true,
Irules::COLON => seen_colon = true,
_ if !child.is_named() => {}
_ if seen_colon => alternative = alternative.or(Some(child)),
_ if seen_question => consequence = consequence.or(Some(child)),
_ => condition = Some(child),
}
}
(condition, consequence, alternative)
}
fn irules_walk_ternary(node: &Node, conditions: &mut f64) {
let (condition, consequence, alternative) = irules_ternary_slots(node);
if let Some(condition) = condition {
irules_count_condition(&condition, conditions);
}
for branch in [consequence, alternative].into_iter().flatten() {
count_negated_operand(
&branch,
irules_wrapper_operand,
irules_condition_scores_itself,
conditions,
);
}
}
fn irules_condition_expr<'a>(node: &Node<'a>) -> Option<Node<'a>> {
node.children()
.find(|child| matches!(child.kind_id().into(), Irules::Expr))
}
impl Abc for IrulesCode {
fn compute<'a>(
node: &Node<'a>,
code: &'a [u8],
ancestors: Ancestors<'a, '_>,
stats: &mut Stats,
) {
match node.kind_id().into() {
Irules::Set => {
stats.assignments += 1.;
}
Irules::Command => {
if irules_command_is_assignment(node, code) {
stats.assignments += 1.;
} else {
stats.branches += 1.;
}
}
Irules::EQEQ
| Irules::BANGEQ
| Irules::LT
| Irules::GT
| Irules::LTEQ
| Irules::GTEQ
| Irules::Eq
| Irules::Ne
| Irules::StartsWith
| Irules::EndsWith
| Irules::Contains
| Irules::Equals
| Irules::Matches
| Irules::MatchesRegex
| Irules::MatchesGlob
| Irules::In
| Irules::Ni
| Irules::Else => {
stats.conditions += 1.;
}
Irules::If | Irules::While => {
if let Some(expr) = irules_condition_expr(node) {
irules_count_condition(&expr, &mut stats.conditions);
}
}
Irules::Elseif => {
stats.conditions += 1.;
if let Some(expr) = irules_condition_expr(node) {
irules_count_condition(&expr, &mut stats.conditions);
}
}
Irules::TernaryExpr => {
stats.conditions += 1.;
irules_walk_ternary(node, &mut stats.conditions);
}
Irules::AMPAMP | Irules::PIPEPIPE | Irules::And | Irules::Or => {
if let Some(chain) = ancestors.parent(node) {
irules_count_chain_operands(&chain, &mut stats.conditions);
}
}
_ => {}
}
}
}
const IRULES_ASSIGNMENT_COMMANDS: &[&str] = &["incr", "append", "lappend"];
fn irules_command_is_assignment(node: &Node, code: &[u8]) -> bool {
let Some(first) = node.child(0) else {
return false;
};
first
.utf8_text(code)
.map(crate::lang_helpers::strip_global_qualifier)
.is_some_and(|word| IRULES_ASSIGNMENT_COMMANDS.contains(&word))
}
fn irules_wrapper_operand<'a>(node: &Node<'a>) -> Option<(Node<'a>, bool)> {
match node.kind_id().into() {
Irules::Expr => wrapped_operand(node).map(|o| (o, false)),
Irules::UnaryExpr
if node
.child(0)
.is_some_and(|c| matches!(c.kind_id().into(), Irules::BANG | Irules::Not)) =>
{
wrapped_operand(node).map(|o| (o, true))
}
_ => None,
}
}
fn irules_condition_scores_itself(expr: &Node) -> bool {
match expr.kind_id().into() {
Irules::TernaryExpr => true,
Irules::BinopExpr => expr.children().any(|token| {
matches!(
token.kind_id().into(),
Irules::EQEQ
| Irules::BANGEQ
| Irules::LT
| Irules::GT
| Irules::LTEQ
| Irules::GTEQ
| Irules::Eq
| Irules::Ne
| Irules::StartsWith
| Irules::EndsWith
| Irules::Contains
| Irules::Equals
| Irules::Matches
| Irules::MatchesRegex
| Irules::MatchesGlob
| Irules::In
| Irules::Ni
| Irules::AMPAMP
| Irules::PIPEPIPE
| Irules::And
| Irules::Or
)
}),
_ => false,
}
}
fn irules_count_condition(condition: &Node, conditions: &mut f64) {
count_boolean_slot(
condition,
irules_wrapper_operand,
irules_condition_scores_itself,
conditions,
);
}
fn irules_count_chain_operands(chain: &Node, conditions: &mut f64) {
if chain.kind_id() == Irules::BinopExpr {
count_each_operand(chain, irules_count_condition, conditions);
}
}