code-moniker-check 0.7.1

Rules engine for code-moniker: DSL, rule config/profiles, evaluation over the symbol graph, and suppression.
Documentation
use super::ast::*;
use super::cursor::{self, ParseResult, ParserState};
use super::error::ParseError;

pub(super) fn starts_metric_call(state: &ParserState<'_>) -> bool {
	metric_prefix(state).is_some()
}

pub(super) fn try_parse_metric_expr<'a>(
	state: ParserState<'a>,
) -> ParseResult<'a, Option<NumberExpr>> {
	let Some((name, kind)) = metric_prefix(&state) else {
		return Ok((None, state));
	};
	let state = cursor::advance(state, name.len());
	let (binding, state) = parse_metric_call_body(state, name)?;
	Ok((Some(NumberExpr::Metric { kind, binding }), state))
}

fn metric_prefix(state: &ParserState<'_>) -> Option<(&'static str, MetricKind)> {
	let rest = cursor::rest(state);
	[
		("fan_out", MetricKind::FanOut),
		("fan_in", MetricKind::FanIn),
		("lcom4", MetricKind::Lcom4),
		("cbo", MetricKind::Cbo),
		("rfc", MetricKind::Rfc),
		("wmc", MetricKind::Wmc),
		("dit", MetricKind::Dit),
		("noc", MetricKind::Noc),
	]
	.into_iter()
	.find(|(name, _)| {
		rest.strip_prefix(*name)
			.is_some_and(|after| after.starts_with('('))
	})
}

fn parse_metric_call_body<'a>(state: ParserState<'a>, name: &str) -> ParseResult<'a, Binding> {
	if cursor::peek_byte(&state) != Some(b'(') {
		return Err(cursor::bail(&state, format!("expected `(` after `{name}`")));
	}
	let state = cursor::advance(state, 1);
	let state = cursor::skip_ws(state);
	let (raw, state) = cursor::take_alpha_token(state);
	let binding = match raw {
		"self" => Binding::Self_,
		"each" => Binding::Each,
		_ => {
			return Err(ParseError::BadExpr {
				expr: cursor::raw(&state).to_string(),
				msg: format!("unknown metric binding `{raw}` (allowed: self, each)"),
			});
		}
	};
	let state = cursor::skip_ws(state);
	if cursor::peek_byte(&state) != Some(b')') {
		return Err(cursor::bail(
			&state,
			format!(
				"missing `)` for `{name}` at byte {}",
				cursor::position(&state)
			),
		));
	}
	Ok((binding, cursor::advance(state, 1)))
}