use std::collections::{BTreeMap, BTreeSet};
use anyhow::Result;
use ruff_db::system::{SystemPath, SystemPathBuf};
use ruff_linter::source_kind::SourceKind;
use ruff_python_ast::helpers::to_module_path;
use ruff_python_parser::{ParseOptions, parse};
pub use ty_module_resolver::ResolverEnvironment;
use crate::collector::Collector;
pub use crate::db::{ModuleDb, resolve_search_paths};
use crate::resolver::Resolver;
pub use crate::settings::{AnalyzeSettings, Direction, StringImports};
mod collector;
mod db;
mod resolver;
mod settings;
#[derive(Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ModuleImports(BTreeSet<SystemPathBuf>);
impl ModuleImports {
pub fn detect<'db>(
db: &'db ModuleDb,
environment: ResolverEnvironment<'db>,
source: &SourceKind,
path: &SystemPath,
package: Option<&SystemPath>,
string_imports: StringImports,
type_checking_imports: bool,
) -> Result<Self> {
let parse_options = ParseOptions::from(source.py_source_type())
.with_target_version(environment.python_version(db));
let parsed = parse(source.source_code(), parse_options)?;
let module_path =
package.and_then(|package| to_module_path(package.as_std_path(), path.as_std_path()));
let imports = Collector::new(
module_path.as_deref(),
string_imports,
type_checking_imports,
)
.collect(parsed.syntax());
let mut resolved_imports = ModuleImports::default();
let resolver = Resolver::new(db, path, environment);
for import in imports {
for resolved in resolver.resolve(import) {
if let Some(path) = resolved.as_system_path() {
resolved_imports.insert(path.to_path_buf());
}
}
}
Ok(resolved_imports)
}
fn insert(&mut self, path: SystemPathBuf) {
self.0.insert(path);
}
pub fn extend(&mut self, paths: impl IntoIterator<Item = SystemPathBuf>) {
self.0.extend(paths);
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn relative_to(self, path: &SystemPath) -> Self {
Self(
self.0
.into_iter()
.map(|import| {
import
.strip_prefix(path)
.map(SystemPath::to_path_buf)
.unwrap_or(import)
})
.collect(),
)
}
}
#[derive(Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ImportMap(BTreeMap<SystemPathBuf, ModuleImports>);
impl ImportMap {
pub fn dependencies(imports: impl IntoIterator<Item = (SystemPathBuf, ModuleImports)>) -> Self {
let mut map = ImportMap::default();
for (path, imports) in imports {
map.0.insert(path, imports);
}
map
}
pub fn dependents(imports: impl IntoIterator<Item = (SystemPathBuf, ModuleImports)>) -> Self {
let mut reverse = ImportMap::default();
for (path, imports) in imports {
for import in imports.0 {
reverse.0.entry(import).or_default().insert(path.clone());
}
reverse.0.entry(path).or_default();
}
reverse
}
}