use regex::Regex;
use std::collections::HashMap;
use std::collections::HashSet;
use std::process::Command;
use std::str;
use serde_json::Value;
pub(crate) fn ruff_graph(
as_pkgs: bool,
as_dependents: bool,
paths: Option<Vec<String>>,
) -> HashMap<String, HashSet<String>> {
let graph_output = Command::new("ruff")
.args(["analyze", "graph", "--preview"])
.args(if as_dependents {
Vec::<&str>::new()
} else {
vec!["--direction", "dependents"]
})
.args(paths.unwrap_or_default())
.output()
.expect("failed to execute process");
let j: Value =
serde_json::from_str::<Value>(str::from_utf8(&graph_output.stdout).unwrap()).unwrap();
j.as_object()
.unwrap()
.clone()
.into_iter()
.map(|(k, v)| {
if as_pkgs {
(
path_to_module(&k),
v.as_array()
.unwrap()
.iter()
.map(|i| path_to_module(i.as_str().unwrap()))
.collect::<HashSet<_>>(),
)
} else {
(
k,
v.as_array()
.unwrap()
.iter()
.map(|i| i.as_str().unwrap().to_string())
.collect::<HashSet<_>>(),
)
}
})
.collect::<HashMap<_, _>>()
}
fn path_to_module(path: &str) -> String {
let _module_path_with_extensions = path.replace("/__init__.py", "").replace("/", ".");
let full_module_path: String = Regex::new(r"\.py$")
.unwrap()
.replace(&_module_path_with_extensions, "")
.into();
match full_module_path.find("src.") {
Some(src_index) => {
let start_index = src_index + 4; full_module_path[start_index..].to_string()
}
None => full_module_path,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_path_to_module() {
assert_eq!(path_to_module("foo/src/foo/bar.py"), "foo.bar");
assert_eq!(path_to_module("foo/src/foo/bar/__init__.py"), "foo.bar");
assert_eq!(path_to_module("foo/src/foo/__init__.py"), "foo");
}
}