code-moniker-workspace 0.4.0

Workspace model, ports, snapshots, linkage, and change analysis for code-moniker.
Documentation
use code_moniker_core::core::moniker::{Moniker, Segment};

use crate::snapshot::ReferenceRecord;
use crate::source::CodeIndexMaterial;

pub(in crate::linkage) struct LinkageQuery<'a> {
	pub(in crate::linkage) material: &'a CodeIndexMaterial,
	pub(in crate::linkage) target: &'a Moniker,
	pub(in crate::linkage) target_first: Option<Segment<'a>>,
	pub(in crate::linkage) target_last: Option<Segment<'a>>,
	pub(in crate::linkage) target_segment_count: usize,
	pub(in crate::linkage) reference_kind: &'a str,
	pub(in crate::linkage) call_name: Option<&'a str>,
	pub(in crate::linkage) call_arity: Option<usize>,
	pub(in crate::linkage) confidence: Option<&'a str>,
	pub(in crate::linkage) source_file: usize,
}

#[derive(Clone, Copy)]
pub(in crate::linkage) struct ReferenceLocation {
	pub(in crate::linkage) source_file: usize,
	pub(in crate::linkage) reference: usize,
}

pub(in crate::linkage) struct ReferenceLocations {
	ordered: Vec<ReferenceLocation>,
	by_file: Vec<Vec<usize>>,
}

impl ReferenceLocations {
	pub(in crate::linkage) fn from_material(material: &CodeIndexMaterial) -> Self {
		let mut ordered = Vec::new();
		let mut by_file = Vec::with_capacity(material.files.len());
		for (source_file, file) in material.files.iter().enumerate() {
			let mut file_references = Vec::with_capacity(file.graph.ref_count());
			for reference in 0..file.graph.ref_count() {
				let reference_idx = ordered.len();
				ordered.push(ReferenceLocation {
					source_file,
					reference,
				});
				file_references.push(reference_idx);
			}
			by_file.push(file_references);
		}
		Self { ordered, by_file }
	}

	pub(in crate::linkage) fn get(&self, reference_idx: usize) -> Option<ReferenceLocation> {
		self.ordered.get(reference_idx).copied()
	}

	pub(in crate::linkage) fn reference_idx(
		&self,
		source_file: usize,
		reference: usize,
	) -> Option<usize> {
		self.by_file.get(source_file)?.get(reference).copied()
	}
}

impl<'a> LinkageQuery<'a> {
	pub(in crate::linkage) fn new(
		reference: &'a ReferenceRecord,
		material: &'a CodeIndexMaterial,
	) -> Option<Self> {
		let (source_file, reference_idx) = material.identity.reference_location(&reference.id)?;
		Self::at(
			reference,
			material,
			ReferenceLocation {
				source_file,
				reference: reference_idx,
			},
		)
	}

	pub(in crate::linkage) fn at(
		reference: &'a ReferenceRecord,
		material: &'a CodeIndexMaterial,
		location: ReferenceLocation,
	) -> Option<Self> {
		let file = material.files.get(location.source_file)?;
		if location.reference >= file.graph.ref_count() {
			return None;
		}
		let target = &file.graph.ref_at(location.reference).target;
		let segment_summary = segment_summary(target);
		Some(Self {
			material,
			target,
			target_first: segment_summary.first,
			target_last: segment_summary.last,
			target_segment_count: segment_summary.count,
			reference_kind: reference.kind.as_str(),
			call_name: reference.call_name.as_deref(),
			call_arity: reference.call_arity,
			confidence: reference.confidence.as_deref(),
			source_file: location.source_file,
		})
	}

	pub(in crate::linkage) fn with_target(&self, target: &'a Moniker) -> Self {
		let segment_summary = segment_summary(target);
		Self {
			target,
			target_first: segment_summary.first,
			target_last: segment_summary.last,
			target_segment_count: segment_summary.count,
			..*self
		}
	}

	pub(in crate::linkage) fn target_segments(&self) -> impl Iterator<Item = Segment<'a>> + '_ {
		self.target.as_view().segments()
	}
}

struct SegmentSummary<'a> {
	first: Option<Segment<'a>>,
	last: Option<Segment<'a>>,
	count: usize,
}

fn segment_summary(target: &Moniker) -> SegmentSummary<'_> {
	let mut summary = SegmentSummary {
		first: None,
		last: None,
		count: 0,
	};
	for segment in target.as_view().segments() {
		if summary.first.is_none() {
			summary.first = Some(segment);
		}
		summary.last = Some(segment);
		summary.count += 1;
	}
	summary
}