acp/commands/
output.rs

1//! @acp:module "Command Output Formatting"
2//! @acp:summary "Shared output formatting utilities for CLI commands"
3//! @acp:domain cli
4//! @acp:layer service
5//!
6//! Provides tree rendering, symbol formatting, and colored output.
7
8use console::{style, StyledObject};
9
10use crate::cache::SymbolType;
11use crate::constraints::LockLevel;
12
13/// Tree renderer with box-drawing characters
14pub struct TreeRenderer {
15    pub use_unicode: bool,
16}
17
18impl Default for TreeRenderer {
19    fn default() -> Self {
20        Self { use_unicode: true }
21    }
22}
23
24impl TreeRenderer {
25    /// Branch character for intermediate items
26    pub fn branch(&self) -> &'static str {
27        if self.use_unicode {
28            "├─"
29        } else {
30            "|-"
31        }
32    }
33
34    /// Branch character for last item
35    pub fn last_branch(&self) -> &'static str {
36        if self.use_unicode {
37            "└─"
38        } else {
39            "`-"
40        }
41    }
42
43    /// Vertical continuation line
44    pub fn vertical(&self) -> &'static str {
45        if self.use_unicode {
46            "│ "
47        } else {
48            "| "
49        }
50    }
51
52    /// Horizontal separator line
53    pub fn separator(&self, width: usize) -> String {
54        if self.use_unicode {
55            "━".repeat(width)
56        } else {
57            "=".repeat(width)
58        }
59    }
60}
61
62/// Format a symbol reference with type abbreviation and line number
63pub fn format_symbol_ref(name: &str, symbol_type: &SymbolType, line: usize) -> String {
64    let type_abbrev = match symbol_type {
65        SymbolType::Function => "fn",
66        SymbolType::Method => "method",
67        SymbolType::Class => "class",
68        SymbolType::Interface => "iface",
69        SymbolType::Type => "type",
70        SymbolType::Enum => "enum",
71        SymbolType::Struct => "struct",
72        SymbolType::Trait => "trait",
73        SymbolType::Const => "const",
74    };
75    format!("{} ({}:{})", name, type_abbrev, line)
76}
77
78/// Format a symbol reference with line range
79pub fn format_symbol_ref_range(name: &str, symbol_type: &SymbolType, lines: [usize; 2]) -> String {
80    let type_abbrev = match symbol_type {
81        SymbolType::Function => "fn",
82        SymbolType::Method => "method",
83        SymbolType::Class => "class",
84        SymbolType::Interface => "iface",
85        SymbolType::Type => "type",
86        SymbolType::Enum => "enum",
87        SymbolType::Struct => "struct",
88        SymbolType::Trait => "trait",
89        SymbolType::Const => "const",
90    };
91    format!("{} ({}:{}-{})", name, type_abbrev, lines[0], lines[1])
92}
93
94/// Format constraint level with color
95pub fn format_constraint_level(level: &LockLevel) -> StyledObject<&'static str> {
96    match level {
97        LockLevel::Frozen => style("frozen").red().bold(),
98        LockLevel::Restricted => style("restricted").yellow(),
99        LockLevel::ApprovalRequired => style("approval-required").yellow(),
100        LockLevel::TestsRequired => style("tests-required").cyan(),
101        LockLevel::DocsRequired => style("docs-required").cyan(),
102        LockLevel::ReviewRequired => style("review-required").cyan(),
103        LockLevel::Normal => style("normal").green(),
104        LockLevel::Experimental => style("experimental").magenta(),
105    }
106}
107
108/// Format constraint level as plain string
109pub fn constraint_level_str(level: &LockLevel) -> &'static str {
110    match level {
111        LockLevel::Frozen => "frozen",
112        LockLevel::Restricted => "restricted",
113        LockLevel::ApprovalRequired => "approval-required",
114        LockLevel::TestsRequired => "tests-required",
115        LockLevel::DocsRequired => "docs-required",
116        LockLevel::ReviewRequired => "review-required",
117        LockLevel::Normal => "normal",
118        LockLevel::Experimental => "experimental",
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_tree_renderer() {
128        let renderer = TreeRenderer::default();
129        assert_eq!(renderer.branch(), "├─");
130        assert_eq!(renderer.last_branch(), "└─");
131        assert_eq!(renderer.vertical(), "│ ");
132    }
133
134    #[test]
135    fn test_format_symbol_ref() {
136        assert_eq!(
137            format_symbol_ref("validate", &SymbolType::Function, 45),
138            "validate (fn:45)"
139        );
140        assert_eq!(
141            format_symbol_ref("User", &SymbolType::Class, 10),
142            "User (class:10)"
143        );
144    }
145
146    #[test]
147    fn test_format_symbol_ref_range() {
148        assert_eq!(
149            format_symbol_ref_range("validate", &SymbolType::Function, [45, 89]),
150            "validate (fn:45-89)"
151        );
152    }
153}