use std::sync::Arc;
use petgraph::graph::NodeIndex;
use crate::datatypes::values::{raw_string, Value};
use crate::graph::dir_graph::DirGraph;
use crate::graph::embedder::Embedder;
use crate::graph::schema;
use crate::graph::storage::GraphRead;
use crate::graph::{SourceLocation, SourceLookup};
pub const CODE_TYPES: &[&str] = &[
"Function",
"Struct",
"Class",
"Mixin",
"Enum",
"Trait",
"Protocol",
"Interface",
"Module",
"Constant",
];
pub fn resolve_code_entity(
dir: &Arc<DirGraph>,
name: &str,
node_type: Option<&str>,
) -> (Option<NodeIndex>, Vec<(NodeIndex, schema::NodeInfo)>) {
let name_val = Value::String(name.to_string());
let types_to_search: Vec<&str> = match node_type {
Some(nt) => vec![nt],
None => CODE_TYPES.to_vec(),
};
for nt in &types_to_search {
if let Some(indices) = dir.type_indices.get(nt) {
for idx in indices.iter() {
if let Some(node) = dir.get_node(idx) {
if *node.id() == name_val {
return (Some(idx), Vec::new());
}
}
}
}
}
if name.contains("::") {
let suffix = format!("::{}", name);
let mut matches: Vec<(NodeIndex, schema::NodeInfo)> = Vec::new();
for nt in &types_to_search {
if let Some(indices) = dir.type_indices.get(nt) {
for idx in indices.iter() {
if let Some(node) = dir.get_node(idx) {
if let Value::String(qn) = &*node.id() {
if qn.ends_with(&suffix) {
matches.push((idx, node.to_node_info(&dir.interner)));
}
}
}
}
}
}
if matches.len() == 1 {
return (Some(matches[0].0), matches);
} else if !matches.is_empty() {
return (None, matches);
}
}
let mut matches: Vec<(NodeIndex, schema::NodeInfo)> = Vec::new();
for nt in &types_to_search {
if let Some(indices) = dir.type_indices.get(nt) {
for idx in indices.iter() {
if let Some(node) = dir.get_node(idx) {
let name_match = node
.get_field_ref("name")
.map(|v| *v == name_val)
.unwrap_or(false)
|| node
.get_field_ref("title")
.map(|v| *v == name_val)
.unwrap_or(false);
if name_match {
matches.push((idx, node.to_node_info(&dir.interner)));
}
}
}
}
}
if matches.len() == 1 {
(Some(matches[0].0), matches)
} else {
(None, matches)
}
}
pub fn infer_selection_node_type(
selection: &crate::graph::schema::CowSelection,
dir: &Arc<DirGraph>,
) -> Option<String> {
let level_idx = selection.get_level_count().saturating_sub(1);
let level = selection.get_level(level_idx)?;
let first_idx = level.iter_node_indices().next()?;
dir.graph
.node_weight(first_idx)
.map(|n| n.node_type_str(&dir.interner).to_string())
}
pub fn discover_property_keys_from_data(
nodes: &[(&str, &crate::graph::schema::NodeData)],
interner: &crate::graph::schema::StringInterner,
) -> Vec<String> {
let mut seen = std::collections::HashSet::new();
let mut keys = Vec::new();
for (_, node) in nodes {
for key in node.property_keys(interner) {
if seen.insert(key.to_string()) {
keys.push(key.to_string());
}
}
}
keys.sort();
keys
}
pub fn source_location(dir: &Arc<DirGraph>, name: &str, node_type: Option<&str>) -> SourceLookup {
let (resolved, matches) = resolve_code_entity(dir, name, node_type);
if let Some(target_idx) = resolved {
let node = match dir.get_node(target_idx) {
Some(n) => n,
None => return SourceLookup::NotFound,
};
let type_name = node.get_node_type_ref(&dir.interner).to_string();
let entity_name = raw_string(&node.title());
let qname = raw_string(&node.id());
let file_path = node.get_field_ref("file_path").as_deref().map(raw_string);
let line_number = node
.get_field_ref("line_number")
.as_deref()
.and_then(|v| match v {
Value::Int64(n) => Some(*n),
_ => None,
});
let end_line = node
.get_field_ref("end_line")
.as_deref()
.and_then(|v| match v {
Value::Int64(n) => Some(*n),
_ => None,
});
let signature = node.get_field_ref("signature").as_deref().map(raw_string);
SourceLookup::Found(SourceLocation {
type_name,
name: entity_name,
qualified_name: qname,
file_path,
line_number,
end_line,
signature,
})
} else if matches.is_empty() {
SourceLookup::NotFound
} else {
let qnames: Vec<String> = matches
.iter()
.map(|(_, info)| raw_string(&info.id))
.collect();
SourceLookup::Ambiguous(qnames)
}
}
pub struct KnowledgeGraph {
inner: Arc<DirGraph>,
embedder: Option<Arc<dyn Embedder>>,
}
impl KnowledgeGraph {
pub fn from_arc(inner: Arc<DirGraph>) -> Self {
KnowledgeGraph {
inner,
embedder: None,
}
}
pub fn dir(&self) -> &Arc<DirGraph> {
&self.inner
}
pub fn set_embedder_native(&mut self, embedder: Arc<dyn Embedder>) {
self.embedder = Some(embedder);
}
pub fn embedder(&self) -> Option<&Arc<dyn Embedder>> {
self.embedder.as_ref()
}
pub fn source_location(&self, name: &str, node_type: Option<&str>) -> SourceLookup {
source_location(&self.inner, name, node_type)
}
}