1use std::collections::BTreeMap;
2
3#[derive(Clone, Debug, Eq, PartialEq)]
5pub struct ModuleHelpEntry {
6 pub category: String,
8 pub name: String,
10 pub short: String,
12}
13
14#[must_use]
16pub fn build_root_long(intro: &str, entries: &[ModuleHelpEntry], has_guide: bool) -> String {
17 let mut categories = Vec::<String>::new();
18 let mut by_category = BTreeMap::<String, Vec<&ModuleHelpEntry>>::new();
19 for entry in entries {
20 if !by_category.contains_key(&entry.category) {
21 categories.push(entry.category.clone());
22 }
23 by_category
24 .entry(entry.category.clone())
25 .or_default()
26 .push(entry);
27 }
28
29 let max_width = entries
30 .iter()
31 .map(|entry| entry.name.len())
32 .max()
33 .unwrap_or_default();
34 let mut out = intro.to_owned();
35 for category in categories {
36 out.push_str(&format!("\n\n {category}:"));
37 if let Some(category_entries) = by_category.get(&category) {
38 for entry in category_entries {
39 out.push_str(&format!(
40 "\n {:<width$} {}",
41 entry.name,
42 entry.short,
43 width = max_width
44 ));
45 }
46 }
47 }
48 out.push_str("\n\n Find Commands:");
49 out.push_str("\n --search <keyword> Search all commands and guides by keyword");
50 out.push_str("\n tree Display full command tree");
51 if has_guide {
52 out.push_str("\n guide Built-in guides for AI agents and developers");
53 }
54 out
55}