use std::collections::HashMap;
use tree_sitter::Node;
use crate::core::moniker::Moniker;
use crate::lang::sdk::{DiscoveredDef, ResolvedRef, TypeExpr};
use super::defs::{collect_defs, predeclare_types};
use super::imports::{ImportedPackage, collect_imports};
use super::refs::collect_refs;
pub(super) type CallableTable = HashMap<(Moniker, Vec<u8>), Vec<u8>>;
pub(super) type ReturnTypeTable = HashMap<(Moniker, Vec<u8>), TypeExpr>;
pub(super) type FieldTypeTable = HashMap<(Moniker, Vec<u8>), TypeExpr>;
pub(super) struct DiscoveredGoFile {
pub root: Moniker,
pub defs: Vec<DiscoveredDef>,
pub refs: Vec<ResolvedRef>,
}
pub(super) struct GoDiscover<'src> {
pub(super) root: Moniker,
pub(super) source: &'src [u8],
pub(super) deep: bool,
pub(super) defs: Vec<DiscoveredDef>,
pub(super) refs: Vec<ResolvedRef>,
pub(super) imports: Vec<ImportedPackage>,
pub(super) type_table: HashMap<Vec<u8>, Moniker>,
pub(super) callables: CallableTable,
pub(super) return_types: ReturnTypeTable,
pub(super) field_types: FieldTypeTable,
}
impl<'src> GoDiscover<'src> {
pub fn run(
root: Moniker,
source: &'src [u8],
deep: bool,
root_node: Node<'_>,
) -> DiscoveredGoFile {
let mut discover = Self {
root: root.clone(),
source,
deep,
defs: Vec::new(),
refs: Vec::new(),
imports: Vec::new(),
type_table: HashMap::new(),
callables: CallableTable::new(),
return_types: ReturnTypeTable::new(),
field_types: FieldTypeTable::new(),
};
collect_imports(&mut discover, root_node, &root);
predeclare_types(&mut discover, root_node, &root);
collect_defs(&mut discover, root_node, &root);
collect_refs(&mut discover, root_node, &root);
DiscoveredGoFile {
root,
defs: discover.defs,
refs: discover.refs,
}
}
pub(super) fn push_def(&mut self, def: DiscoveredDef) {
if !self
.defs
.iter()
.any(|existing| existing.moniker == def.moniker)
{
self.defs.push(def);
}
}
pub(super) fn push_ref(&mut self, reference: ResolvedRef) {
if !self
.refs
.iter()
.any(|existing| same_ref(existing, &reference))
{
self.refs.push(reference);
}
}
}
fn same_ref(left: &ResolvedRef, right: &ResolvedRef) -> bool {
left.source == right.source
&& left.target == right.target
&& left.kind == right.kind
&& left.position == right.position
&& left.confidence == right.confidence
}