use std::collections::BTreeMap;
use std::sync::Arc;
use pedant_syntax::go::{GoDeclarationKind, GoSignatureRole};
use crate::resolution::go::facts::GoSourceFacts;
use crate::resolution::go::signature_fact::GoSignatureTermRecord;
use crate::resolution::go::unit::GoResolutionUnit;
use crate::resolution::identity::position;
use super::corpus::{Corpus, UnitSource};
use super::error::GoResolutionError;
use super::imports::FileImports;
use super::index::Index;
use super::lookup::{self, Outcome};
use super::types;
use super::universe;
struct Written<'a> {
unit: usize,
path: &'a Arc<str>,
facts: &'a GoSourceFacts,
imports: &'a FileImports<'a>,
}
impl<'a> Written<'a> {
fn of(unit: usize, held: UnitSource<'a>) -> Self {
Self {
unit,
path: held.path,
facts: held.source.facts(),
imports: held.imports,
}
}
fn canonical(&self, index: &Index, corpus: &Corpus<'_>, declaration: u32) -> Option<Box<str>> {
let parameters =
self.role_text(index, corpus, (declaration, GoSignatureRole::Parameter))?;
let results = self.role_text(index, corpus, (declaration, GoSignatureRole::Result))?;
Some(format!("({parameters})({results})").into_boxed_str())
}
fn role_text(
&self,
index: &Index,
corpus: &Corpus<'_>,
stated: (u32, GoSignatureRole),
) -> Option<Box<str>> {
let (declaration, role) = stated;
let named: Option<Box<[Box<str>]>> = self
.facts
.signature_terms()
.iter()
.filter(|term| term.declaration() == declaration && term.role() == role)
.map(|term| self.term_type(index, corpus, term))
.collect();
Some(named?.join(",").into_boxed_str())
}
fn term_type(
&self,
index: &Index,
corpus: &Corpus<'_>,
term: &GoSignatureTermRecord,
) -> Option<Box<str>> {
let name = term.type_name()?;
let package = self.package_of(index, corpus, (term.type_qualifier(), name))?;
let variadic = match term.variadic() {
true => "...",
false => "",
};
let pointer = match term.pointer() {
true => "*",
false => "",
};
Some(format!("{variadic}{pointer}{package}{name}").into_boxed_str())
}
fn package_of(
&self,
index: &Index,
corpus: &Corpus<'_>,
written: (Option<&str>, &str),
) -> Option<Box<str>> {
let (qualifier, name) = written;
match qualifier {
Some(package) => self
.imports
.named(package)
.map(|target| prefix(target.path)),
None => self.declared_package(index, corpus, name),
}
}
fn declared_package(&self, index: &Index, corpus: &Corpus<'_>, name: &str) -> Option<Box<str>> {
match self.declaring_unit(index, name) {
Some(unit) => corpus.unit(unit).map(|found| prefix(found.import_path())),
None => universe::is_predeclared(name).then(|| Box::from("")),
}
}
fn declaring_unit(&self, index: &Index, name: &str) -> Option<usize> {
let Outcome::Found(found) = lookup::bare(index, (self.unit, self.imports), name) else {
return None;
};
types::unique_type(index, &found, |_, slot| slot.unit)
}
}
#[derive(Default)]
pub(super) struct Signatures {
stated: BTreeMap<usize, Box<str>>,
}
impl Signatures {
pub(super) fn of(index: &Index, corpus: &Corpus<'_>) -> Result<Self, GoResolutionError> {
let mut signatures = Self::default();
for (unit, held) in corpus.units().iter().enumerate() {
signatures.state_unit(index, corpus, (unit, held))?;
}
Ok(signatures)
}
fn state_unit(
&mut self,
index: &Index,
corpus: &Corpus<'_>,
stated: (usize, &GoResolutionUnit),
) -> Result<(), GoResolutionError> {
let (unit, held) = stated;
for source in corpus.sources_of(held)?.iter() {
self.state_source(index, corpus, &Written::of(unit, *source));
}
Ok(())
}
fn state_source(&mut self, index: &Index, corpus: &Corpus<'_>, written: &Written<'_>) {
for (declaration, record) in written.facts.declarations().iter().enumerate() {
let ordinal = position(declaration);
let canonical = is_callable(record.kind())
.then(|| written.canonical(index, corpus, ordinal))
.flatten();
let slot = index
.unit(written.unit)
.and_then(|tables| tables.declared(written.path, ordinal));
if let Some((slot, canonical)) = slot.zip(canonical) {
self.stated.insert(slot, canonical);
}
}
}
pub(super) fn of_slot(&self, slot: usize) -> Option<&str> {
self.stated.get(&slot).map(|stated| &**stated)
}
}
fn is_callable(kind: GoDeclarationKind) -> bool {
match kind {
GoDeclarationKind::Function
| GoDeclarationKind::Method
| GoDeclarationKind::InterfaceMethod => true,
GoDeclarationKind::Struct
| GoDeclarationKind::Interface
| GoDeclarationKind::DefinedType
| GoDeclarationKind::TypeAlias
| GoDeclarationKind::Constant
| GoDeclarationKind::Variable
| GoDeclarationKind::Field
| GoDeclarationKind::EmbeddedField => false,
}
}
fn prefix(path: &str) -> Box<str> {
format!("{path}.").into_boxed_str()
}