use std::sync::Arc;
use pedant_types::{ReferenceKind, ResolutionReport, ResolutionUnit, ResolutionUnitId, SymbolKind};
use crate::resolution::binding::{self, BindingFault};
use crate::resolution::line_index::{LineIndex, SourceLines, keyed_lines, source_lines};
use crate::resolution::rust::fingerprint::RustSnapshotFingerprint;
use crate::resolution::rust::identity::{PackageId, TargetId};
use crate::resolution::rust::snapshot::{
RustResolutionSnapshot, RustResolutionUnit, RustSnapshotUnitId,
};
use crate::resolution::rust::warning;
use crate::resolution::sites::{self, SiteError};
use super::error::RustResolutionError;
pub(super) fn unit_key(unit: &RustResolutionUnit) -> Arc<str> {
warning::unit_key(unit)
}
pub(super) fn crate_name(unit: &RustResolutionUnit) -> Arc<str> {
Arc::from(unit.name().replace('-', "_"))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RustUnitBinding {
unit: ResolutionUnitId,
snapshot_unit: RustSnapshotUnitId,
package: PackageId,
target: TargetId,
}
impl RustUnitBinding {
pub fn unit(&self) -> ResolutionUnitId {
self.unit
}
pub fn snapshot_unit(&self) -> RustSnapshotUnitId {
self.snapshot_unit
}
pub fn package(&self) -> PackageId {
self.package
}
pub fn target(&self) -> TargetId {
self.target
}
#[cfg(feature = "resolution-test-support")]
pub(in crate::resolution::rust) fn rebound(&self, snapshot_unit: RustSnapshotUnitId) -> Self {
Self {
snapshot_unit,
..*self
}
}
}
#[derive(Debug, Clone)]
pub struct RustTargetResolution {
root_target: TargetId,
snapshot_fingerprint: RustSnapshotFingerprint,
units: Box<[RustUnitBinding]>,
report: Arc<ResolutionReport>,
}
impl RustTargetResolution {
pub fn try_new(
snapshot: &RustResolutionSnapshot,
report: ResolutionReport,
) -> Result<Self, RustResolutionError> {
let lines = source_lines(snapshot.sources());
Self::bound(snapshot, report, &lines)
}
pub(super) fn try_new_indexed<'snapshot>(
snapshot: &'snapshot RustResolutionSnapshot,
report: ResolutionReport,
lines: Box<[LineIndex<'snapshot>]>,
) -> Result<Self, RustResolutionError> {
let keyed = keyed_lines(snapshot.sources(), lines);
Self::bound(snapshot, report, &keyed)
}
fn bound(
snapshot: &RustResolutionSnapshot,
report: ResolutionReport,
lines: &SourceLines<'_>,
) -> Result<Self, RustResolutionError> {
let units = bind_units(snapshot, report.units())?;
validate_definition_kinds(&report)?;
validate_reference_kinds(&report)?;
validate_snapshot_sites(lines, &report)?;
Ok(Self {
root_target: snapshot.root_target(),
snapshot_fingerprint: snapshot.fingerprint(),
units,
report: Arc::new(report),
})
}
pub fn root_target(&self) -> TargetId {
self.root_target
}
pub fn snapshot_fingerprint(&self) -> RustSnapshotFingerprint {
self.snapshot_fingerprint
}
pub fn units(&self) -> &[RustUnitBinding] {
&self.units
}
pub fn unit(&self, unit: ResolutionUnitId) -> Option<&RustUnitBinding> {
self.units.get(usize::try_from(unit.index()).ok()?)
}
pub fn report(&self) -> &ResolutionReport {
&self.report
}
pub fn shared_report(&self) -> Arc<ResolutionReport> {
Arc::clone(&self.report)
}
#[cfg(feature = "resolution-test-support")]
pub(in crate::resolution::rust) fn with_unit_bindings(
&self,
units: Box<[RustUnitBinding]>,
) -> Self {
Self {
root_target: self.root_target,
snapshot_fingerprint: self.snapshot_fingerprint,
units,
report: Arc::clone(&self.report),
}
}
}
fn bind_units(
snapshot: &RustResolutionSnapshot,
units: &[ResolutionUnit],
) -> Result<Box<[RustUnitBinding]>, RustResolutionError> {
binding::bind_by_key(units, snapshot.units(), unit_key, bound).map_err(unit_refusal)
}
fn bound(unit: &ResolutionUnit, found: &RustResolutionUnit) -> RustUnitBinding {
RustUnitBinding {
unit: unit.id(),
snapshot_unit: found.id(),
package: found.package(),
target: found.target(),
}
}
fn unit_refusal(fault: BindingFault) -> RustResolutionError {
match fault {
BindingFault::UnitCountMismatch { report, snapshot } => {
RustResolutionError::UnitCountMismatch { report, snapshot }
}
BindingFault::UnknownKey { unit } => RustResolutionError::UnitMapping {
unit,
reason: Box::from("no snapshot unit carries this key"),
},
}
}
fn validate_definition_kinds(report: &ResolutionReport) -> Result<(), RustResolutionError> {
match binding::first_unsupported_definition(report, is_rust_definition_kind) {
None => Ok(()),
Some((definition, kind)) => {
Err(RustResolutionError::UnsupportedDefinitionKind { definition, kind })
}
}
}
fn validate_reference_kinds(report: &ResolutionReport) -> Result<(), RustResolutionError> {
match binding::first_unsupported_reference(report, is_rust_reference_kind) {
None => Ok(()),
Some((reference, kind)) => {
Err(RustResolutionError::UnsupportedReferenceKind { reference, kind })
}
}
}
fn is_rust_definition_kind(kind: SymbolKind) -> bool {
match kind {
SymbolKind::Module
| SymbolKind::Function
| SymbolKind::Method
| SymbolKind::Struct
| SymbolKind::Enum
| SymbolKind::Union
| SymbolKind::Trait
| SymbolKind::TypeAlias
| SymbolKind::Constant
| SymbolKind::Static => true,
SymbolKind::Package
| SymbolKind::Interface
| SymbolKind::DefinedType
| SymbolKind::Variable
| SymbolKind::Field => false,
}
}
fn is_rust_reference_kind(kind: ReferenceKind) -> bool {
match kind {
ReferenceKind::Module
| ReferenceKind::Import
| ReferenceKind::Call
| ReferenceKind::Type
| ReferenceKind::Implementation => true,
ReferenceKind::Value => false,
}
}
fn validate_snapshot_sites(
lines: &SourceLines<'_>,
report: &ResolutionReport,
) -> Result<(), RustResolutionError> {
sites::validate_sites(lines, report).map_err(refusal)
}
fn refusal(error: SiteError) -> RustResolutionError {
match error {
SiteError::UnknownFile { file } => RustResolutionError::UnknownFile { file },
SiteError::InvalidCoordinate { file, line, column } => {
RustResolutionError::InvalidCoordinate { file, line, column }
}
}
}