mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-21 13:38:30 +00:00
feat: add dcr tree command to visualize dependencies
This commit is contained in:
@@ -12,6 +12,7 @@ pub fn help() -> i32 {
|
||||
println!(" init Initialize the current directory as a project");
|
||||
println!(" build [--profile] Build the project (default: --debug)");
|
||||
println!(" run [--profile] Build and run the project (default: --debug)");
|
||||
println!(" tree Display the dependency tree");
|
||||
println!(" test Run project tests (alias: tests)");
|
||||
println!(" clean Remove the target directory");
|
||||
println!(" gen <subcommand> Generate IDE integration files");
|
||||
|
||||
@@ -10,3 +10,4 @@ pub mod new;
|
||||
pub mod run;
|
||||
pub mod setup;
|
||||
pub mod test;
|
||||
pub mod tree;
|
||||
|
||||
117
src/cli/tree.rs
Normal file
117
src/cli/tree.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use crate::core::config::Config;
|
||||
use crate::utils::log::error;
|
||||
use crate::utils::text::{BOLD_CYAN, printc};
|
||||
use std::collections::HashSet;
|
||||
use std::path::{Path, PathBuf};
|
||||
use toml::Value;
|
||||
|
||||
pub fn tree(_args: &[String]) -> i32 {
|
||||
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
let config = match Config::open("dcr.toml") {
|
||||
Ok(c) => c,
|
||||
Err(_) => {
|
||||
error("dcr.toml not found in current directory");
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
let name = config
|
||||
.get("package.name")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("unknown");
|
||||
let version = config
|
||||
.get("package.version")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("0.0.0");
|
||||
|
||||
printc(&format!("{} v{}", name, version), BOLD_CYAN);
|
||||
|
||||
let mut seen = HashSet::new();
|
||||
seen.insert(name.to_string());
|
||||
|
||||
if let Some(deps) = config.get("dependencies").and_then(|v| v.as_table()) {
|
||||
print_deps(deps, ¤t_dir, "", &mut seen);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn print_deps(deps: &toml::value::Table, base_path: &Path, prefix: &str, seen: &mut HashSet<String>) {
|
||||
let mut dep_list: Vec<_> = deps.iter().collect();
|
||||
dep_list.sort_by_key(|(name, _)| *name);
|
||||
|
||||
for (i, (name, value)) in dep_list.iter().enumerate() {
|
||||
let is_last = i == dep_list.len() - 1;
|
||||
let connector = if is_last { "└── " } else { "├── " };
|
||||
let child_prefix = if is_last { " " } else { "│ " };
|
||||
|
||||
let dep_path = resolve_dep_path(value, base_path);
|
||||
let mut version = String::new();
|
||||
let mut sub_deps = None;
|
||||
let mut resolved_path = None;
|
||||
|
||||
if let Some(path) = &dep_path {
|
||||
let dcr_toml = path.join("dcr.toml");
|
||||
if dcr_toml.exists() {
|
||||
if let Ok(config) = Config::open(&dcr_toml.to_string_lossy()) {
|
||||
if let Some(v) = config.get("package.version").and_then(|v| v.as_str()) {
|
||||
version = format!(" v{}", v);
|
||||
}
|
||||
sub_deps = config.get("dependencies").and_then(|v| v.as_table()).cloned();
|
||||
resolved_path = Some(path.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if version.is_empty() {
|
||||
version = match value {
|
||||
Value::String(s) if s.starts_with("git:") || s.starts_with("github:") => {
|
||||
format!(" ({})", s)
|
||||
}
|
||||
Value::Table(t) => {
|
||||
if let Some(v) = t.get("version").and_then(|v| v.as_str()) {
|
||||
format!(" v{}", v)
|
||||
} else if let Some(git) = t.get("git").and_then(|v| v.as_str()) {
|
||||
format!(" ({})", git)
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
_ => "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
println!("{}{}{}{}", prefix, connector, name, version);
|
||||
|
||||
if let Some(s_deps) = sub_deps {
|
||||
if !seen.contains(*name) {
|
||||
seen.insert(name.to_string());
|
||||
let new_prefix = format!("{}{}", prefix, child_prefix);
|
||||
if let Some(path) = resolved_path {
|
||||
print_deps(&s_deps, &path, &new_prefix, seen);
|
||||
}
|
||||
seen.remove(*name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_dep_path(value: &Value, base_path: &Path) -> Option<PathBuf> {
|
||||
match value {
|
||||
Value::String(s) => {
|
||||
if s.starts_with("path:") {
|
||||
Some(base_path.join(&s[5..]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Value::Table(t) => {
|
||||
if let Some(path) = t.get("path").and_then(|v| v.as_str()) {
|
||||
Some(base_path.join(path))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ fn main() {
|
||||
"add" => cli::add::add(rest),
|
||||
"build" => cli::build::build(rest),
|
||||
"run" => cli::run::run(rest),
|
||||
"tree" => cli::tree::tree(rest),
|
||||
"test" | "tests" => cli::test::test(rest),
|
||||
"clean" => cli::clean::clean(rest),
|
||||
"gen" => cli::r#gen::r#gen(rest),
|
||||
|
||||
Reference in New Issue
Block a user