use std::sync::Arc;
use pedant_types::{ReferenceKind, ResolutionReport, ResolutionUnit, ResolutionUnitId, SymbolKind};
use crate::resolution::binding::{self, BindingFault};
use crate::resolution::go::fingerprint::GoSnapshotFingerprint;
use crate::resolution::go::snapshot::GoResolutionSnapshot;
use crate::resolution::go::snapshot_module::GoSnapshotModuleId;
use crate::resolution::go::unit::{GoPackageContext, GoResolutionUnit, GoSnapshotUnitId};
use crate::resolution::line_index::source_lines;
use crate::resolution::sites::{self, SiteError};
use super::error::GoResolutionError;
pub(super) fn unit_key(unit: &GoResolutionUnit) -> Arc<str> {
Arc::from(format!("{}#{}", unit.import_path(), unit.context().token()))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GoUnitBinding {
unit: ResolutionUnitId,
snapshot_unit: GoSnapshotUnitId,
module: GoSnapshotModuleId,
context: GoPackageContext,
}
impl GoUnitBinding {
pub fn unit(&self) -> ResolutionUnitId {
self.unit
}
pub fn snapshot_unit(&self) -> GoSnapshotUnitId {
self.snapshot_unit
}
pub fn module(&self) -> GoSnapshotModuleId {
self.module
}
pub fn context(&self) -> GoPackageContext {
self.context
}
}
#[derive(Debug, Clone)]
pub struct GoProjectResolution {
root_module: GoSnapshotModuleId,
snapshot_fingerprint: GoSnapshotFingerprint,
units: Box<[GoUnitBinding]>,
report: Arc<ResolutionReport>,
}
impl GoProjectResolution {
pub fn try_new(
snapshot: &GoResolutionSnapshot,
report: ResolutionReport,
) -> Result<Self, GoResolutionError> {
let units = bind_units(snapshot, report.units())?;
validate_definition_kinds(&report)?;
validate_reference_kinds(&report)?;
validate_snapshot_sites(snapshot, &report)?;
Ok(Self {
root_module: snapshot.root_module(),
snapshot_fingerprint: snapshot.fingerprint(),
units,
report: Arc::new(report),
})
}
pub fn root_module(&self) -> GoSnapshotModuleId {
self.root_module
}
pub fn snapshot_fingerprint(&self) -> GoSnapshotFingerprint {
self.snapshot_fingerprint
}
pub fn units(&self) -> &[GoUnitBinding] {
&self.units
}
pub fn unit(&self, unit: ResolutionUnitId) -> Option<&GoUnitBinding> {
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)
}
}
fn bind_units(
snapshot: &GoResolutionSnapshot,
units: &[ResolutionUnit],
) -> Result<Box<[GoUnitBinding]>, GoResolutionError> {
binding::bind_by_key(units, snapshot.units(), unit_key, bound).map_err(unit_refusal)
}
fn bound(unit: &ResolutionUnit, found: &GoResolutionUnit) -> GoUnitBinding {
GoUnitBinding {
unit: unit.id(),
snapshot_unit: found.id(),
module: found.module(),
context: found.context(),
}
}
fn unit_refusal(fault: BindingFault) -> GoResolutionError {
match fault {
BindingFault::UnitCountMismatch { report, snapshot } => {
GoResolutionError::UnitCountMismatch { report, snapshot }
}
BindingFault::UnknownKey { unit } => GoResolutionError::UnitMapping {
unit,
reason: Box::from("no snapshot package context carries this key"),
},
}
}
fn validate_definition_kinds(report: &ResolutionReport) -> Result<(), GoResolutionError> {
match binding::first_unsupported_definition(report, is_go_definition_kind) {
None => Ok(()),
Some((definition, kind)) => {
Err(GoResolutionError::UnsupportedDefinitionKind { definition, kind })
}
}
}
fn validate_reference_kinds(report: &ResolutionReport) -> Result<(), GoResolutionError> {
match binding::first_unsupported_reference(report, is_go_reference_kind) {
None => Ok(()),
Some((reference, kind)) => {
Err(GoResolutionError::UnsupportedReferenceKind { reference, kind })
}
}
}
fn is_go_definition_kind(kind: SymbolKind) -> bool {
match kind {
SymbolKind::Package
| SymbolKind::Function
| SymbolKind::Method
| SymbolKind::Struct
| SymbolKind::Interface
| SymbolKind::DefinedType
| SymbolKind::TypeAlias
| SymbolKind::Constant
| SymbolKind::Variable
| SymbolKind::Field => true,
SymbolKind::Module
| SymbolKind::Enum
| SymbolKind::Union
| SymbolKind::Trait
| SymbolKind::Static => false,
}
}
fn is_go_reference_kind(kind: ReferenceKind) -> bool {
match kind {
ReferenceKind::Import
| ReferenceKind::Call
| ReferenceKind::Type
| ReferenceKind::Value
| ReferenceKind::Implementation => true,
ReferenceKind::Module => false,
}
}
fn validate_snapshot_sites(
snapshot: &GoResolutionSnapshot,
report: &ResolutionReport,
) -> Result<(), GoResolutionError> {
sites::validate_sites(&source_lines(snapshot.sources()), report).map_err(refusal)
}
fn refusal(error: SiteError) -> GoResolutionError {
match error {
SiteError::UnknownFile { file } => GoResolutionError::UnknownFile { file },
SiteError::InvalidCoordinate { file, line, column } => {
GoResolutionError::InvalidCoordinate { file, line, column }
}
}
}