mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-21 14: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!(" init Initialize the current directory as a project");
|
||||||
println!(" build [--profile] Build the project (default: --debug)");
|
println!(" build [--profile] Build the project (default: --debug)");
|
||||||
println!(" run [--profile] Build and run 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!(" test Run project tests (alias: tests)");
|
||||||
println!(" clean Remove the target directory");
|
println!(" clean Remove the target directory");
|
||||||
println!(" gen <subcommand> Generate IDE integration files");
|
println!(" gen <subcommand> Generate IDE integration files");
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ pub mod new;
|
|||||||
pub mod run;
|
pub mod run;
|
||||||
pub mod setup;
|
pub mod setup;
|
||||||
pub mod test;
|
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),
|
"add" => cli::add::add(rest),
|
||||||
"build" => cli::build::build(rest),
|
"build" => cli::build::build(rest),
|
||||||
"run" => cli::run::run(rest),
|
"run" => cli::run::run(rest),
|
||||||
|
"tree" => cli::tree::tree(rest),
|
||||||
"test" | "tests" => cli::test::test(rest),
|
"test" | "tests" => cli::test::test(rest),
|
||||||
"clean" => cli::clean::clean(rest),
|
"clean" => cli::clean::clean(rest),
|
||||||
"gen" => cli::r#gen::r#gen(rest),
|
"gen" => cli::r#gen::r#gen(rest),
|
||||||
|
|||||||
Reference in New Issue
Block a user