Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

init crate #1

Merged
merged 1 commit into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,23 @@ version = "0.0.0"
edition = "2021"
description = "Visualize crate.io dependencies as a Tree ├──."
license = "MIT"
keywords = ["depth", "tree", "cargo", "crate"]
repository = "/~https://github.com/wiseaidev/depth"
documentation = "https://docs.rs/depth/"
authors = ["Mahmoud Harmouch <oss@wiseai.dev>"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = { version = "4.4.8", features = ["derive"] }
crates_io_api = "0.8.2"
petgraph = "0.6.4"
serde = { version = "1.0.192", features = ["derive"] }
serde_json = "1.0.108"
toml = "0.8.8"

[profile.release]
codegen-units = 1
opt-level = "z"
lto = "thin"
strip = "symbols"
62 changes: 60 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,60 @@
# depth
Visualize crate.io dependencies as a Tree
# Depth

[![Crates.io](https://img.shields.io/crates/v/depth.svg)](https://crates.io/crates/depth)
[![License](https://img.shields.io/crates/l/depth.svg)](https://opensource.org/licenses/MIT)

> 🚀 `depth`: A command-line tool for fetching and visualizing dependency trees for Rust packages.

## 📖 Table of Contents

- [Installation](#-installation)
- [Usage](#-usage)
- [Features](#-features)
- [Examples](#-examples)
- [Contributing](#-contributing)
- [License](#-license)

## 🚀 Installation

To install `depth`, use the following Cargo command:

```bash
cargo install --locked depth
```

## 🛠️ Usage

Use the `depth` command to visualize dependency trees. Here are some examples:

```bash
# Visualize dependencies at level 1
depth -c crate_name -l 2

# Or simply
depth -c crate_name
```

## ✨ Features

- **Fetch and Visualize Dependency Tree**: Fetch and visualize the dependency tree for a given Rust package using the Crates.io API.
- **Command-Line Tool**: Use the `depth` command-line tool to interactively explore and visualize dependencies.

## 🌟 Examples

```bash
# Visualize dependencies for the 'input_yew' crate at level 1
depth -c input_yew

# Or simply

depth -c yew -l 2
```

## 🤝 Contributing

Contributions and feedback are welcome! If you'd like to contribute, report an issue, or suggest an enhancement, please engage with the project on [GitHub](/~https://github.com/wiseaidev/depth).
Your contributions help improve this crate for the community.

## 📄 License

This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
17 changes: 17 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use clap::Parser;

#[derive(Parser, Debug)]
#[command(
author = "Mahmoud Harmouch",
version = "0.0.1",
about = "Visualize crate.io dependencies as a Tree",
name = "Visualize Deps Tree"
)]
pub struct Cli {
/// Sets the package to display
#[arg(short = 'c', long = "crate")]
pub crate_: String,
/// Sets the levels to display.
#[arg(short = 'l', long = "levels", default_value_t = 2)]
pub levels: usize,
}
209 changes: 209 additions & 0 deletions src/dependency_graph.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
//! # dependency_graph
//!
//! The `dependency_graph` module provides functionality to fetch and visualize dependency trees
//! for Rust packages using the Crates.io API and the petgraph crate. With this module, you can
//! explore the dependencies of a given Rust package and visualize the relationship between
//! different packages in a dependency graph.
//!
//! # Quick Start
//!
//! Get started with the `dependency_graph` module by following these simple steps:
//!
//! 1. Import the necessary types and functions into your code:
//!
//! ```rust
//! use depth::dependency_graph::{DependencyGraph, fetch_package_info, Package};
//! use crates_io_api::SyncClient;
//! ```
//!
//! 2. Create an instance of `DependencyGraph` and use it to fetch and visualize dependency trees:
//!
//! ```rust
//! let mut graph = DependencyGraph::new();
//! graph.fetch_dependency_tree("your_package_name", 2);
//! graph.print_dependencies_at_level(/* ... */);
//! ```
//!
//! # Key Features
//!
//! The `dependency_graph` module offers the following key features:
//!
//! - **Fetch Dependency Tree**: Fetch the dependency tree for a given Rust package using the Crates.io API.
//! - **Visualize Dependencies**: Visualize the dependencies of a package in a graph structure using petgraph.
//! - **Print Dependencies**: Print the dependencies of a package at a specified depth in the dependency tree.
//!
//! # Usage
//!
//! ## Fetching Dependency Tree
//!
//! Use the `fetch_dependency_tree` method to fetch and build the dependency tree for a specific package:
//!
//! ```rust
//! let mut graph = DependencyGraph::new();
//! graph.fetch_dependency_tree("your_package_name", 2);
//! ```
//!
//! ## Visualizing Dependencies
//!
//! Utilize the `print_dependencies_at_level` method to print dependencies at a specified depth in the dependency tree:
//!
//! ```rust
//! let package = Package::new(/* ... */);
//! graph.print_dependencies_at_level(&package, 0, 2);
//! ```
//!
//! # Examples
//!
//! ```rust
//! use depth::dependency_graph::{DependencyGraph, Package};
//! use crates_io_api::SyncClient;
//!
//! let mut graph = DependencyGraph::new();
//! graph.fetch_dependency_tree("your_package_name", 2);
//! // Additional functionality with the dependency graph...
//! ```

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;

/// A struct representing a dependency graph.
#[derive(Debug)]
pub struct DependencyGraph {
/// The underlying directed graph.
graph: DiGraph<(String, String), &'static str>,
}

impl Default for DependencyGraph {
fn default() -> Self {
Self::new()
}
}

impl DependencyGraph {
/// Creates a new instance of `DependencyGraph`.
pub fn new() -> Self {
DependencyGraph {
graph: DiGraph::new(),
}
}

/// Fetches the dependency tree for a given package.
///
/// # Arguments
///
/// * `package_name` - The name of the package to fetch.
/// * `depth` - The maximum depth to fetch dependencies.
///
/// # Returns
///
/// Returns `Ok(Some(package))` if the package is fetched successfully,
/// `Ok(None)` if the package does not exist, and `Err` on an error.
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,
)
}

/// Adds a package and its dependencies to the graph.
///
/// # Arguments
///
/// * `package` - The package to add to the graph.
///
/// # Returns
///
/// Returns the `NodeIndex` of the added package.
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
}

/// Adds a dependency edge between two packages in the graph.
///
/// # Arguments
///
/// * `source` - The `NodeIndex` of the source package.
/// * `target` - The `NodeIndex` of the target package.
pub fn add_dependency_edge(&mut self, source: NodeIndex, target: NodeIndex) {
self.graph.add_edge(source, target, "depends");
}

/// Prints the dependencies of a package up to a specified level.
///
/// # Arguments
///
/// * `package` - The package to print dependencies for.
/// * `depth` - The current depth in the dependency tree.
/// * `max_depth` - The maximum depth to print dependencies.
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);
}
}
}
}

/// Generates a DOT format representation of the graph.
///
/// # Returns
///
/// Returns a `String` containing the DOT format representation.
pub fn to_dot(&self) -> String {
format!(
"{:?}",
Dot::with_config(&self.graph, &[Config::GraphContentOnly])
)
}
}
79 changes: 79 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//! # depth
//!
//! The `depth` crate provides a command-line tool for fetching and visualizing dependency trees
//! for Rust packages using the Crates.io API and the `petgraph` crate. With this tool, you can explore
//! the dependencies of a given Rust package and visualize the relationship between different packages
//! in a dependency graph.
//!
//! # Quick Start
//!
//! Get started with the `depth` crate by following these simple steps:
//!
//! 1. Install the `depth` tool using Cargo:
//!
//! ```bash
//! cargo install --locked depth
//! ```
//!
//! 2. Use the `depth` command to visualize dependency trees:
//!
//! ```bash
//! # Visualize dependencies at level 1
//! depth -c crate_name -l 1
//!
//! or simply:
//!
//! depth -c crate_name
//! ```
//!
//! # Key Features
//!
//! The `depth` crate offers the following key features:
//!
//! - **Fetch and Visualize Dependency Tree**: Fetch and visualize the dependency tree for a given Rust package using the Crates.io API.
//! - **Command-Line Tool**: Use the `depth` command-line tool to interactively explore and visualize dependencies.
//!
//! # GitHub Repository
//!
//! You can access the source code for this crate on [GitHub](/~https://github.com/wiseaidev/depth).
//!
//! # Contributing
//!
//! Contributions and feedback are welcome! If you'd like to contribute, report an issue, or suggest an enhancement,
//! please engage with the project on [GitHub](/~https://github.com/wiseaidev/depth).
//! Your contributions help improve this crate for the community.

pub mod cli;
pub mod dependency_graph;
pub mod package;

use std::error::Error;

use dependency_graph::DependencyGraph;

/// Visualizes the dependency tree for a given package.
///
/// # Arguments
///
/// * `package_name` - The name of the package to visualize.
/// * `depth` - The depth up to which dependencies should be visualized.
///
/// # Returns
///
/// A Result indicating success or an error if the visualization process fails.
pub fn visualize_dependency_tree(package_name: &str, depth: usize) -> Result<(), Box<dyn Error>> {
let mut graph = DependencyGraph::new();

if let Some(root_package) = graph.fetch_dependency_tree(package_name, depth)? {
// Print dependencies
println!("Dependencies for package '{}':", package_name);
graph.print_dependencies_at_level(&root_package, 0, depth);

// Visualize the graph (commented out for now)
// println!("{}", graph.to_dot());
} else {
eprintln!("Package not found or does not have a Cargo.toml file");
}

Ok(())
}
Loading