use std::sync::Arc;
use pedant_types::{ResolutionRecord, SymbolDefinition, SymbolReference};
use crate::edge::GraphEdgeKind;
use crate::error::GraphBuildError;
use crate::reference::GraphReferenceKind;
use super::fragment::{
CandidateProjection, DefinitionProjection, PlacedSource, ReferenceProjection, SourceFragment,
};
use super::identity::{DefinitionIdentity, DefinitionTable};
use super::mapping::{self, Vocabulary};
use super::validation;
#[derive(Default)]
pub(crate) struct SourceRecords<'a> {
pub(crate) definitions: Vec<(u32, &'a SymbolDefinition)>,
pub(crate) references: Vec<(&'a SymbolReference, &'a ResolutionRecord)>,
}
pub(crate) struct StatedSource<'a> {
pub(crate) table: &'a DefinitionTable,
pub(crate) vocabulary: &'a Vocabulary,
pub(crate) placed: &'a PlacedSource,
pub(crate) records: &'a SourceRecords<'a>,
}
pub(crate) fn derived_projection(
stated: &StatedSource<'_>,
) -> Result<SourceFragment, GraphBuildError> {
let definitions: Box<[DefinitionProjection]> = stated
.records
.definitions
.iter()
.copied()
.map(|reported| projected_definition(stated, reported))
.collect::<Result<_, GraphBuildError>>()?;
let references: Box<[ReferenceProjection]> = stated
.records
.references
.iter()
.copied()
.map(|reported| projected_reference(stated.table, reported))
.collect::<Result<_, GraphBuildError>>()?;
Ok(SourceFragment {
source: stated.placed.source.shared(),
definitions,
references,
})
}
fn projected_definition(
stated: &StatedSource<'_>,
reported: (u32, &SymbolDefinition),
) -> Result<DefinitionProjection, GraphBuildError> {
let (at, definition) = reported;
Ok(DefinitionProjection {
identity: Arc::clone(validation::definition_identity(stated.table, at)?),
language: definition.language(),
kind: stated.vocabulary.definition(definition.kind()),
parent: projected_join(
stated.table,
definition.parent().map(|parent| parent.index()),
)?,
})
}
fn projected_join(
table: &DefinitionTable,
stated: Option<u32>,
) -> Result<Option<Arc<DefinitionIdentity>>, GraphBuildError> {
match stated {
None => Ok(None),
Some(at) => Ok(Some(Arc::clone(validation::definition_identity(
table, at,
)?))),
}
}
fn projected_reference(
table: &DefinitionTable,
reported: (&SymbolReference, &ResolutionRecord),
) -> Result<ReferenceProjection, GraphBuildError> {
let (reference, record) = reported;
let kind = mapping::reference_kind(reference.kind());
Ok(ReferenceProjection {
language: reference.language(),
kind,
text: Arc::from(reference.text()),
span: SymbolReference::span(reference).clone(),
gaps: record.gaps().into(),
enclosing: projected_join(
table,
reference
.enclosing_definition()
.map(|enclosing| enclosing.index()),
)?,
candidates: projected_candidates(table, (record, mapping::candidate_edge_kind(kind)))?,
})
}
fn projected_candidates(
table: &DefinitionTable,
answered: (&ResolutionRecord, GraphEdgeKind),
) -> Result<Box<[CandidateProjection]>, GraphBuildError> {
let (record, kind) = answered;
record
.candidates()
.iter()
.map(|candidate| {
let target = validation::definition_identity(table, candidate.definition().index())?;
Ok(CandidateProjection {
target: Arc::clone(target),
kind,
certainty: mapping::certainty(candidate.certainty()),
})
})
.collect()
}
pub(crate) fn states_current_claim(held: &SourceFragment, stated: &StatedSource<'_>) -> bool {
held.source == stated.placed.source
&& states_definitions(&held.definitions, stated)
&& states_references(&held.references, stated.table, &stated.records.references)
}
fn states_definitions(held: &[DefinitionProjection], stated: &StatedSource<'_>) -> bool {
let reported = &stated.records.definitions;
held.len() == reported.len()
&& held
.iter()
.zip(reported.iter().copied())
.all(|(projection, definition)| states_definition(projection, stated, definition))
}
fn states_definition(
held: &DefinitionProjection,
stated: &StatedSource<'_>,
reported: (u32, &SymbolDefinition),
) -> bool {
let (at, definition) = reported;
stated.table.identity(at) == Some(&held.identity)
&& held.language == definition.language()
&& stated
.vocabulary
.states_definition(&held.kind, definition.kind())
&& states_join(
held.parent.as_ref(),
stated.table,
definition.parent().map(|parent| parent.index()),
)
}
fn states_references(
held: &[ReferenceProjection],
table: &DefinitionTable,
reported: &[(&SymbolReference, &ResolutionRecord)],
) -> bool {
held.len() == reported.len()
&& held
.iter()
.zip(reported.iter().copied())
.all(|(projection, reference)| states_reference(projection, table, reference))
}
fn states_reference(
held: &ReferenceProjection,
table: &DefinitionTable,
reported: (&SymbolReference, &ResolutionRecord),
) -> bool {
let (reference, record) = reported;
let kind = mapping::reference_kind(reference.kind());
held.language == reference.language()
&& held.kind == kind
&& *held.text == *reference.text()
&& held.span == *SymbolReference::span(reference)
&& *held.gaps == *record.gaps()
&& states_join(
held.enclosing.as_ref(),
table,
reference
.enclosing_definition()
.map(|enclosing| enclosing.index()),
)
&& states_candidates(&held.candidates, table, (record, kind))
}
fn states_join(
held: Option<&Arc<DefinitionIdentity>>,
table: &DefinitionTable,
stated: Option<u32>,
) -> bool {
match (held, stated) {
(None, None) => true,
(Some(identity), Some(at)) => table.identity(at) == Some(identity),
(None, Some(_)) | (Some(_), None) => false,
}
}
fn states_candidates(
held: &[CandidateProjection],
table: &DefinitionTable,
answered: (&ResolutionRecord, GraphReferenceKind),
) -> bool {
let (record, kind) = answered;
let edge = mapping::candidate_edge_kind(kind);
held.len() == record.candidates().len()
&& held
.iter()
.zip(record.candidates())
.all(|(candidate, stated)| {
candidate.kind == edge
&& candidate.certainty == mapping::certainty(stated.certainty())
&& table.identity(stated.definition().index()) == Some(&candidate.target)
})
}