use crate::package::{fetch_package_info, Package};
use crates_io_api::SyncClient;
use petgraph::dot::{Config, Dot};
use petgraph::graph::{DiGraph, NodeIndex};
use std::collections::HashMap;
#[derive(Debug)]
pub struct DependencyGraph {
graph: DiGraph<(String, String), &'static str>,
}
impl Default for DependencyGraph {
fn default() -> Self {
Self::new()
}
}
impl DependencyGraph {
pub fn new() -> Self {
DependencyGraph {
graph: DiGraph::new(),
}
}
pub fn fetch_dependency_tree(
&mut self,
package_name: &str,
depth: usize,
) -> Result<Option<Package>, Box<dyn std::error::Error>> {
let mut visited_packages = HashMap::new();
let client = SyncClient::new(
"my-user-agent (my-contact@domain.com)",
std::time::Duration::from_millis(1000),
)
.unwrap();
fetch_package_info(
&(package_name.to_string(), "".to_string()),
&mut visited_packages,
self,
&client,
depth,
)
}
pub fn add_package_to_graph(&mut self, package: &Package) -> NodeIndex {
let node_index = self
.graph
.add_node((package.name.clone(), package.url.clone()));
for dependency in &package.dependencies {
if !self
.graph
.node_indices()
.any(|i| self.graph[i] == *dependency)
{
self.graph.add_node(dependency.clone());
}
}
node_index
}
pub fn add_dependency_edge(&mut self, source: NodeIndex, target: NodeIndex) {
self.graph.add_edge(source, target, "depends");
}
pub fn print_dependencies_at_level(&self, package: &Package, depth: usize, max_depth: usize) {
if depth < max_depth {
println!(
"{:indent$} ├── {} - ({})",
"",
package.name,
package.url,
indent = depth * 2
);
for dependency in &package.dependencies {
let child_index = self
.graph
.node_indices()
.find(|&index| &self.graph[index] == dependency)
.unwrap_or_else(NodeIndex::end);
if child_index != NodeIndex::end() {
let child_package = Package::new(
self.graph[child_index].clone().0,
self.graph[child_index].clone().1,
vec![("".to_string(), "".to_string())],
false,
);
self.print_dependencies_at_level(&child_package, depth + 1, max_depth);
}
}
}
}
pub fn to_dot(&self) -> String {
format!(
"{:?}",
Dot::with_config(&self.graph, &[Config::GraphContentOnly])
)
}
}