use std::sync::Arc;
use crate::Language;
use super::definition::SymbolKind;
use super::error::ResolutionReportError;
use super::finish::assemble;
use super::handle::{
BuilderBrand, CandidateInput, DefinitionHandle, ReferenceHandle, ResolutionUnitHandle,
};
use super::record::{ResolutionCertainty, ResolutionGap};
use super::reference::ReferenceKind;
use super::report::{ResolutionReport, ResolutionTier};
use super::span::SourceSpan;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ResolutionReportLimits {
pub max_units: u32,
pub max_definitions: u32,
pub max_references: u32,
}
impl Default for ResolutionReportLimits {
fn default() -> Self {
Self {
max_units: u32::MAX,
max_definitions: u32::MAX,
max_references: u32::MAX,
}
}
}
impl ResolutionReportLimits {
pub fn admit_unit(&self, count: usize) -> Result<u32, ResolutionReportError> {
admit(count, self.max_units, |limit| {
ResolutionReportError::UnitCapacityExceeded { limit }
})
}
pub fn admit_definition(&self, count: usize) -> Result<u32, ResolutionReportError> {
admit(count, self.max_definitions, |limit| {
ResolutionReportError::DefinitionCapacityExceeded { limit }
})
}
pub fn admit_reference(&self, count: usize) -> Result<u32, ResolutionReportError> {
admit(count, self.max_references, |limit| {
ResolutionReportError::ReferenceCapacityExceeded { limit }
})
}
}
fn admit(
count: usize,
limit: u32,
exceeded: impl Fn(u32) -> ResolutionReportError,
) -> Result<u32, ResolutionReportError> {
u32::try_from(count)
.ok()
.filter(|next| *next < limit)
.ok_or_else(|| exceeded(limit))
}
pub(crate) struct DraftUnit {
pub(crate) language: Language,
pub(crate) key: Arc<str>,
pub(crate) name: Arc<str>,
}
pub(crate) struct DraftDefinition {
pub(crate) unit: u32,
pub(crate) kind: SymbolKind,
pub(crate) name: Arc<str>,
pub(crate) span: SourceSpan,
pub(crate) parent: Option<u32>,
}
pub(crate) struct DraftReference {
pub(crate) unit: u32,
pub(crate) kind: ReferenceKind,
pub(crate) text: Arc<str>,
pub(crate) span: SourceSpan,
pub(crate) enclosing: Option<u32>,
}
pub(crate) struct DraftResolution {
pub(crate) candidates: Box<[(u32, ResolutionCertainty)]>,
pub(crate) gaps: Box<[ResolutionGap]>,
}
pub struct ResolutionReportBuilder {
brand: Arc<BuilderBrand>,
tier: ResolutionTier,
limits: ResolutionReportLimits,
units: Vec<DraftUnit>,
definitions: Vec<DraftDefinition>,
references: Vec<DraftReference>,
resolutions: Vec<Option<DraftResolution>>,
}
impl ResolutionReportBuilder {
pub fn new(tier: ResolutionTier, limits: ResolutionReportLimits) -> Self {
Self {
brand: Arc::new(BuilderBrand),
tier,
limits,
units: Vec::new(),
definitions: Vec::new(),
references: Vec::new(),
resolutions: Vec::new(),
}
}
pub fn add_unit(
&mut self,
language: Language,
key: Arc<str>,
name: Arc<str>,
) -> Result<ResolutionUnitHandle, ResolutionReportError> {
let index = self.limits.admit_unit(self.units.len())?;
self.units.push(DraftUnit {
language,
key,
name,
});
Ok(ResolutionUnitHandle::new(&self.brand, index))
}
pub fn add_definition(
&mut self,
unit: &ResolutionUnitHandle,
kind: SymbolKind,
name: Arc<str>,
span: SourceSpan,
parent: Option<&DefinitionHandle>,
) -> Result<DefinitionHandle, ResolutionReportError> {
let unit = self.local_unit(unit)?;
let parent = self.local_definition_option(parent)?;
let index = self.limits.admit_definition(self.definitions.len())?;
self.definitions.push(DraftDefinition {
unit,
kind,
name,
span,
parent,
});
Ok(DefinitionHandle::new(&self.brand, index))
}
pub fn add_reference(
&mut self,
unit: &ResolutionUnitHandle,
kind: ReferenceKind,
text: Arc<str>,
span: SourceSpan,
enclosing_definition: Option<&DefinitionHandle>,
) -> Result<ReferenceHandle, ResolutionReportError> {
let unit = self.local_unit(unit)?;
let enclosing = self.local_definition_option(enclosing_definition)?;
let index = self.limits.admit_reference(self.references.len())?;
self.references.push(DraftReference {
unit,
kind,
text,
span,
enclosing,
});
self.resolutions.push(None);
Ok(ReferenceHandle::new(&self.brand, index))
}
pub fn set_resolution(
&mut self,
reference: &ReferenceHandle,
candidates: Box<[CandidateInput]>,
gaps: Box<[ResolutionGap]>,
) -> Result<(), ResolutionReportError> {
let index = reference
.resolve(&self.brand)
.ok_or(ResolutionReportError::ForeignReferenceHandle)?;
let stated = candidates
.into_vec()
.into_iter()
.map(|candidate| {
let (definition, certainty) = candidate.into_parts();
self.local_definition(&definition)
.map(|definition| (definition, certainty))
})
.collect::<Result<Vec<_>, _>>()?;
let slot = self
.resolutions
.get_mut(index as usize)
.ok_or(ResolutionReportError::ForeignReferenceHandle)?;
match slot.is_some() {
true => Err(ResolutionReportError::DuplicateResolution { reference: index }),
false => {
*slot = Some(DraftResolution {
candidates: stated.into_boxed_slice(),
gaps,
});
Ok(())
}
}
}
pub fn finish(self) -> Result<ResolutionReport, ResolutionReportError> {
assemble(
self.tier,
self.units,
self.definitions,
self.references,
self.resolutions,
)
}
fn local_unit(&self, unit: &ResolutionUnitHandle) -> Result<u32, ResolutionReportError> {
unit.resolve(&self.brand)
.ok_or(ResolutionReportError::ForeignUnitHandle)
}
fn local_definition(
&self,
definition: &DefinitionHandle,
) -> Result<u32, ResolutionReportError> {
definition
.resolve(&self.brand)
.ok_or(ResolutionReportError::ForeignDefinitionHandle)
}
fn local_definition_option(
&self,
definition: Option<&DefinitionHandle>,
) -> Result<Option<u32>, ResolutionReportError> {
definition
.map(|handle| self.local_definition(handle))
.transpose()
}
}