1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! Dependency tree visualization.
//!
//! This module provides the `cx tree` command which displays the project's
//! dependency graph in a hierarchical, ASCII tree format.
//!
//! ## Example Output
//!
//! ```text
//! my-project v1.0.0
//! ├── raylib (tag: 5.0)
//! ├── json (tag: v3.11.2)
//! └── fmt (git: https://github.com/fmtlib/fmt)
//! ```
use crate::build::load_config;
use anyhow::Result;
use colored::*;
pub fn print_tree() -> Result<()> {
let config = load_config()?;
// Root
println!(
"{} v{}",
config.package.name.bold().cyan(),
config.package.version
);
if let Some(deps) = config.dependencies {
let count = deps.len();
for (i, (name, dep)) in deps.iter().enumerate() {
let is_last = i == count - 1;
let prefix = if is_last { "└──" } else { "├──" };
// Determine version or type
let info = match dep {
crate::config::Dependency::Simple(url) => format!("{}", url.dimmed()),
crate::config::Dependency::Complex {
git,
pkg,
tag,
branch,
rev,
..
} => {
if let Some(t) = tag {
format!("tag: {}", t.green())
} else if let Some(b) = branch {
format!("branch: {}", b.yellow())
} else if let Some(r) = rev {
format!("rev: {:.7}", r.dimmed())
} else if let Some(g) = git {
format!("git: {}", g.dimmed())
} else if let Some(p) = pkg {
format!("pkg: {}", p.cyan())
} else {
"unknown".dimmed().to_string()
}
}
};
println!("{} {} ({})", prefix, name.bold(), info);
// In a real sophisticated tree, we would recursively check lockfiles or
// query the registry for sub-dependencies.
// For now, Caxe is flat or only tracking top-level until we parse vendored deps properly.
// So we stop here.
}
} else {
println!("└── (no dependencies)");
}
Ok(())
}
#[cfg(test)]
mod tests {
use crate::config::Dependency;
#[test]
fn test_dependency_simple_format() {
let dep = Dependency::Simple("https://github.com/raylib/raylib.git".to_string());
match dep {
Dependency::Simple(url) => assert!(url.contains("raylib")),
_ => panic!("Expected Simple variant"),
}
}
#[test]
fn test_dependency_complex_with_tag() {
let dep = Dependency::Complex {
git: Some("https://github.com/nlohmann/json.git".to_string()),
pkg: None,
tag: Some("v3.11.2".to_string()),
branch: None,
rev: None,
build: None,
output: None,
};
match dep {
Dependency::Complex { tag, .. } => {
assert_eq!(tag, Some("v3.11.2".to_string()));
}
_ => panic!("Expected Complex variant"),
}
}
#[test]
fn test_dependency_complex_with_branch() {
let dep = Dependency::Complex {
git: Some("https://github.com/libsdl-org/SDL.git".to_string()),
pkg: None,
tag: None,
branch: Some("SDL2".to_string()),
rev: None,
build: None,
output: None,
};
match dep {
Dependency::Complex { branch, .. } => {
assert_eq!(branch, Some("SDL2".to_string()));
}
_ => panic!("Expected Complex variant"),
}
}
#[test]
fn test_dependency_pkg_config() {
let dep = Dependency::Complex {
git: None,
pkg: Some("gtk+-3.0".to_string()),
tag: None,
branch: None,
rev: None,
build: None,
output: None,
};
match dep {
Dependency::Complex { pkg, .. } => {
assert_eq!(pkg, Some("gtk+-3.0".to_string()));
}
_ => panic!("Expected Complex variant"),
}
}
}