use alloc::string::String;
use alloc::vec::Vec;
use crate::stack::module_path::normalize_module_path;
use crate::stack::ImportBinding;
use crate::syntax::{SyntaxGraph, SyntaxNode};
use crate::{Language, NodeId};
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ImportSite {
pub node: NodeId,
pub scope: NodeId,
pub symbol: String,
pub binding: ImportBinding,
}
#[must_use]
pub fn import_sites(syntax: &SyntaxGraph, language: Language) -> Vec<ImportSite> {
if language != Language::Python {
return Vec::new();
}
let mut sites: Vec<ImportSite> = syntax
.symbol_index
.iter()
.flat_map(|(symbol, scopes)| {
scopes.iter().flat_map(move |(scope, nodes)| {
nodes
.iter()
.filter_map(move |node| python_site(syntax, *node, *scope, symbol))
})
})
.collect();
sites.sort_by_key(|site| site.node);
sites
}
fn python_site(
syntax: &SyntaxGraph,
node: NodeId,
scope: NodeId,
symbol: &str,
) -> Option<ImportSite> {
let Some(SyntaxNode::Import {
expression: Some(expression),
alias,
module_name: None,
..
}) = syntax.nodes.get(&node)
else {
return None;
};
Some(ImportSite {
node,
scope,
symbol: String::from(symbol),
binding: ImportBinding {
module_stem: normalize_module_path(expression),
source_symbol: alias.as_ref().map(|_| last_segment(expression)),
relative_level: 0,
binds_module: false,
},
})
}
fn last_segment(expression: &str) -> String {
String::from(expression.rsplit('.').next().unwrap_or(expression))
}