use crate::builder::command_tree::CommandNode;
use crate::task::{AngrealArg, ANGREAL_ARGS};
pub fn format_arg_signature_pub(args: &[AngrealArg]) -> String {
format_arg_signature(args)
}
fn format_arg_signature(args: &[AngrealArg]) -> String {
args.iter()
.filter_map(|arg| {
let flag = arg
.long
.as_ref()
.map(|l| format!("--{}", l))
.or_else(|| arg.short.map(|s| format!("-{}", s)))?;
if arg.is_flag.unwrap_or(false) {
Some(format!("[{}]", flag))
} else {
let typ = arg.python_type.as_deref().unwrap_or("str");
Some(format!("[{}=<{}>]", flag, typ))
}
})
.collect::<Vec<_>>()
.join(" ")
}
fn get_command_path(node: &CommandNode, parent_path: &[String]) -> String {
if parent_path.is_empty() {
node.name.clone()
} else {
format!("{}.{}", parent_path.join("."), node.name)
}
}
pub fn print_tree(node: &CommandNode, long: bool) {
print_node(node, long, 0, &[]);
}
fn print_node(node: &CommandNode, long: bool, depth: usize, parent_path: &[String]) {
let indent = " ".repeat(depth);
if depth == 0 && node.name == "angreal" {
let mut children: Vec<_> = node.children.iter().collect();
children.sort_by_key(|(name, _)| *name);
for (_, child) in children {
print_node(child, long, depth, parent_path);
}
return;
}
if let Some(cmd) = &node.command {
let command_path = get_command_path(node, parent_path);
let args = ANGREAL_ARGS
.lock()
.unwrap()
.get(&command_path)
.cloned()
.unwrap_or_default();
let arg_sig = format_arg_signature(&args);
let about = cmd.about.as_deref().unwrap_or("");
if arg_sig.is_empty() {
println!("{}{} - {}", indent, node.name, about);
} else {
println!("{}{} {} - {}", indent, node.name, arg_sig, about);
}
if long {
if let Some(tool) = &cmd.tool {
let tool_indent = " ".repeat(depth + 1);
println!();
for line in tool.description.lines() {
if line.trim().is_empty() {
println!();
} else {
println!("{}{}", tool_indent, line);
}
}
println!("{}Risk level: {}", tool_indent, tool.risk_level);
println!();
}
}
} else {
let about = node.about.as_deref().unwrap_or("");
if about.is_empty() {
println!("{}{}:", indent, node.name);
} else {
println!("{}{}: {}", indent, node.name, about);
}
let mut new_parent_path = parent_path.to_vec();
new_parent_path.push(node.name.clone());
let mut children: Vec<_> = node.children.iter().collect();
children.sort_by_key(|(name, _)| *name);
for (_, child) in children {
print_node(child, long, depth + 1, &new_parent_path);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_arg_signature_flag() {
let args = vec![AngrealArg {
name: "verbose".to_string(),
command_name: "test".to_string(),
command_path: "test".to_string(),
is_flag: Some(true),
long: Some("verbose".to_string()),
short: Some('v'),
takes_value: Some(false),
default_value: None,
require_equals: None,
multiple_values: None,
number_of_values: None,
max_values: None,
min_values: None,
python_type: Some("bool".to_string()),
long_help: None,
help: Some("Enable verbose output".to_string()),
required: Some(false),
}];
let sig = format_arg_signature(&args);
assert_eq!(sig, "[--verbose]");
}
#[test]
fn test_format_arg_signature_value() {
let args = vec![AngrealArg {
name: "output".to_string(),
command_name: "test".to_string(),
command_path: "test".to_string(),
is_flag: Some(false),
long: Some("output".to_string()),
short: Some('o'),
takes_value: Some(true),
default_value: None,
require_equals: None,
multiple_values: None,
number_of_values: None,
max_values: None,
min_values: None,
python_type: Some("str".to_string()),
long_help: None,
help: Some("Output file path".to_string()),
required: Some(false),
}];
let sig = format_arg_signature(&args);
assert_eq!(sig, "[--output=<str>]");
}
#[test]
fn test_format_arg_signature_multiple() {
let args = vec![
AngrealArg {
name: "verbose".to_string(),
command_name: "test".to_string(),
command_path: "test".to_string(),
is_flag: Some(true),
long: Some("verbose".to_string()),
short: None,
takes_value: Some(false),
default_value: None,
require_equals: None,
multiple_values: None,
number_of_values: None,
max_values: None,
min_values: None,
python_type: Some("bool".to_string()),
long_help: None,
help: None,
required: Some(false),
},
AngrealArg {
name: "count".to_string(),
command_name: "test".to_string(),
command_path: "test".to_string(),
is_flag: Some(false),
long: Some("count".to_string()),
short: None,
takes_value: Some(true),
default_value: None,
require_equals: None,
multiple_values: None,
number_of_values: None,
max_values: None,
min_values: None,
python_type: Some("int".to_string()),
long_help: None,
help: None,
required: Some(false),
},
];
let sig = format_arg_signature(&args);
assert_eq!(sig, "[--verbose] [--count=<int>]");
}
}