code-moniker-workspace 0.5.0

Workspace model, ports, snapshots, linkage, and change analysis for code-moniker.
Documentation
use code_moniker_core::core::moniker::query::bare_callable_name;
use code_moniker_core::lang::kinds;

use crate::linkage::catalog::{LinkageCandidate, LinkageQuery};
use crate::linkage::language::{LanguageLinkageStrategy, generic::GenericLanguageLinkageStrategy};

pub(super) struct CsharpLanguageLinkageStrategy;

impl LanguageLinkageStrategy for CsharpLanguageLinkageStrategy {
	fn matches(&self, query: &LinkageQuery<'_>, candidate: &LinkageCandidate<'_>) -> bool {
		GenericLanguageLinkageStrategy.matches(query, candidate)
			|| csharp_name_target_matches_def(query, candidate)
	}
}

fn csharp_name_target_matches_def(
	query: &LinkageQuery<'_>,
	candidate: &LinkageCandidate<'_>,
) -> bool {
	let Some(target) = query.target_last else {
		return false;
	};
	let Some(candidate_segment) = candidate.last_segment else {
		return false;
	};
	if is_type_kind(target.kind) && is_type_kind(candidate_segment.kind) {
		return target.name == candidate_segment.name
			|| (query.reference_kind == "annotates"
				&& candidate_segment
					.name
					.strip_suffix(b"Attribute")
					.is_some_and(|short_name| short_name == target.name));
	}
	if !is_target_callable_kind(target.kind) || !is_def_callable_kind(candidate_segment.kind) {
		return false;
	}
	let target_name = query
		.call_name
		.map(str::as_bytes)
		.unwrap_or_else(|| bare_callable_name(target.name));
	let candidate_name = candidate
		.call_name
		.unwrap_or_else(|| bare_callable_name(candidate_segment.name));
	target_name == candidate_name && call_arity_matches(query.call_arity, candidate.call_arity)
}

fn call_arity_matches(call: Option<usize>, definition: Option<usize>) -> bool {
	match (call, definition) {
		(Some(call), Some(definition)) => call == definition,
		_ => true,
	}
}

fn is_type_kind(kind: &[u8]) -> bool {
	matches!(
		kind,
		kinds::CLASS | kinds::INTERFACE | kinds::STRUCT | kinds::RECORD | kinds::ENUM | b"delegate"
	)
}

fn is_target_callable_kind(kind: &[u8]) -> bool {
	matches!(kind, kinds::FUNCTION | kinds::METHOD | kinds::CONSTRUCTOR)
}

fn is_def_callable_kind(kind: &[u8]) -> bool {
	matches!(kind, kinds::METHOD | kinds::CONSTRUCTOR)
}