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::domain::parse_domain_ident;
use super::error::ParseError;

pub(super) fn parse_pair_domain<'a>(state: ParserState<'a>) -> ParseResult<'a, Domain> {
	if !cursor::starts_with(&state, "pairs(") {
		return Err(cursor::bail(
			&state,
			format!("expected `pairs(` at byte {}", cursor::position(&state)),
		));
	}
	let state = cursor::advance(state, "pairs(".len());
	let state = cursor::skip_ws(state);
	let (inner, state) = parse_domain_ident(state)?;
	if matches!(inner, Domain::Pairs(_)) {
		return Err(ParseError::BadExpr {
			expr: cursor::raw(&state).to_string(),
			msg: "nested `pairs(...)` domains are not supported".to_string(),
		});
	}
	let state = cursor::skip_ws(state);
	if cursor::peek_byte(&state) != Some(b')') {
		return Err(cursor::bail(
			&state,
			format!(
				"missing `)` for `pairs(...)` at byte {}",
				cursor::position(&state)
			),
		));
	}
	Ok((Domain::Pairs(Box::new(inner)), cursor::advance(state, 1)))
}

pub(super) fn parse_pair_projection(
	s: &str,
	full: &str,
	pair_bindings_allowed: bool,
) -> Result<Option<PairProjection>, ParseError> {
	let s = s.trim();
	let (side_raw, projection_raw) = match s.split_once('.') {
		Some((side, projection)) => (side, projection),
		None => (s, "self"),
	};
	let side = match side_raw {
		"a" => PairSide::A,
		"b" => PairSide::B,
		_ => return Ok(None),
	};
	if !pair_bindings_allowed {
		return Err(ParseError::BadExpr {
			expr: full.to_string(),
			msg: format!("pair binding `{side_raw}` is only valid inside `pairs(...)` filters"),
		});
	}
	if projection_raw.is_empty() {
		return Err(ParseError::BadExpr {
			expr: full.to_string(),
			msg: format!("pair binding `{s}` needs a projection after `.`"),
		});
	}
	let lhs = pair_projection_lhs(projection_raw).ok_or_else(|| ParseError::BadExpr {
		expr: full.to_string(),
		msg: format!("unknown pair binding projection `{s}`"),
	})?;
	Ok(Some(PairProjection { side, lhs }))
}

fn pair_projection_lhs(projection: &str) -> Option<Lhs> {
	if projection == "self" {
		Some(Lhs::Moniker)
	} else {
		Lhs::from_projection_name(projection)
	}
}