Skip to main content

raps_cli/
credits.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2024-2025 Dmytro Yemelianov
3
4//! Demoscene-style credits display for RAPS CLI.
5//!
6//! Three tiers of branding:
7//! - **Complete** (`raps --version`): full ASCII art + stats
8//! - **Short** (`raps --help` header): one-line brand via `HELP_BRAND`
9//! - **Minimal** (interactive shell welcome): compact stat line
10
11use colored::Colorize;
12
13// ── Constants ───────────────────────────────────────────────────────────────
14
15/// CLI subcommands (Commands enum variants, excluding External and cfg-gated Dashboard)
16pub const COMMAND_COUNT: usize = 27;
17
18/// Workspace crates in Cargo.toml
19pub const WORKSPACE_CRATE_COUNT: usize = 10;
20
21/// APS API services wrapped by service crates
22pub const API_SERVICE_COUNT: usize = 8; // OSS, Derivative, DM, Webhooks, DA, ACC, Reality, Admin
23
24/// One-line brand for `--help` header.
25///
26/// Uses raw ANSI escapes because clap's `help_template` requires `&'static str`.
27/// Same constraint as `GROUPED_COMMANDS_HELP`. The help_template attribute inlines
28/// the same `concat!` expression — this constant documents the canonical form.
29#[allow(dead_code)]
30pub const HELP_BRAND: &str = concat!(
31    "\x1b[1;33mraps\x1b[0m v",
32    env!("CARGO_PKG_VERSION"),
33    " \x1b[32m·\x1b[0m Rust Autodesk Platform Services CLI"
34);
35
36// ── Complete version (--version) ────────────────────────────────────────────
37
38/// Print the full demoscene-style credits block.
39///
40/// Colors are automatically stripped when stdout is not a TTY or `NO_COLOR` is
41/// set, thanks to the `colored` crate.
42pub fn print_version() {
43    let version = env!("CARGO_PKG_VERSION");
44    let mcp_tool_count = crate::mcp::tools::TOOLS.len();
45
46    // Flower
47    println!();
48    println!("      {}", "▄▄▀█▄▄ ▄▄▀█▄".yellow().bold());
49    println!("      {}", "█▄   █▄█   ▀█".yellow().bold());
50    println!("      {}", " ▀█▄▄▀▀▄▄▄▄▄▀".yellow().bold());
51    println!("      {}", "▄▄█▀▀ ▄█▄ ▀▀█".yellow().bold());
52    println!("      {}", "█   ▄█▀ ▀▄▄▄█".yellow().bold());
53    println!("      {}", "▀▀▀▀▀     ▀▀".yellow().bold());
54
55    // RAPS text
56    println!("   {}", "▄▄▄  ▄▄▄  ▄▄▄▄ ▄▄▄▄".yellow().bold());
57    println!("   {}", "█▄▄▀ █▄▄█ █▄▄█ █▄▄▄".yellow().bold());
58    println!("   {}", "█  █ █  █ █    ▄▄▄█".yellow().bold());
59
60    // Heavy separator
61    println!("  {}", "═══════════════════════════════════".green());
62
63    // Subtitle
64    println!("   {}", "Rust Autodesk Platform Services".bright_yellow());
65
66    // Light separator
67    println!(
68        "  {}",
69        "───────────────────────────────────".green().dimmed()
70    );
71
72    // Info block
73    credit_line("version", version);
74    credit_line("author", "dmytro yemelianov");
75    credit_line("license", "Apache-2.0");
76    credit_line("edition", "Rust 2024");
77
78    // Light separator
79    println!(
80        "  {}",
81        "───────────────────────────────────".green().dimmed()
82    );
83
84    // Stats block
85    credit_line("commands", &COMMAND_COUNT.to_string());
86    credit_line("mcp tools", &mcp_tool_count.to_string());
87    credit_line("workspace", &format!("{WORKSPACE_CRATE_COUNT} crates"));
88    credit_line("apis", &format!("{API_SERVICE_COUNT} services"));
89
90    // Light separator
91    println!(
92        "  {}",
93        "───────────────────────────────────".green().dimmed()
94    );
95
96    // Links block
97    credit_line("web", "rapscli.xyz");
98    credit_line("docs", "rapscli.xyz/docs");
99
100    // Heavy separator
101    println!("  {}", "═══════════════════════════════════".green());
102    println!();
103}
104
105/// Print a single credits line with dotted leaders.
106///
107/// Layout: `   {label} ·········· {value}`
108/// Total field width (label + dots) is 20 chars.
109fn credit_line(label: &str, value: &str) {
110    let dot_count = 20usize.saturating_sub(label.len() + 1);
111    let dots = "·".repeat(dot_count);
112    println!(
113        "   {} {} {}",
114        label.dimmed(),
115        dots.green().dimmed(),
116        value.yellow().bold()
117    );
118}
119
120// ── Minimal welcome (shell) ─────────────────────────────────────────────────
121
122/// Print the compact shell welcome line.
123///
124/// Example: `raps v4.12.0 · 27 commands · 94 MCP tools · rapscli.xyz`
125pub fn shell_welcome() {
126    let version = env!("CARGO_PKG_VERSION");
127    let mcp_tool_count = crate::mcp::tools::TOOLS.len();
128    let sep = " · ".green().dimmed();
129
130    println!(
131        "{}{}{}{}{}{}{}",
132        "raps".yellow().bold(),
133        format_args!(" v{version}").to_string().bright_yellow(),
134        sep,
135        format_args!("{COMMAND_COUNT} commands")
136            .to_string()
137            .bright_yellow(),
138        sep,
139        format_args!("{mcp_tool_count} MCP tools")
140            .to_string()
141            .bright_yellow(),
142        format_args!("{sep}rapscli.xyz"),
143    );
144}
145
146// ── Tests ───────────────────────────────────────────────────────────────────
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151
152    /// Sentinel test — fails when commands are added/removed so the constant
153    /// stays in sync with the `Commands` enum.
154    #[test]
155    fn test_command_count() {
156        // If this fails, update COMMAND_COUNT to match the Commands enum
157        // (excluding External and cfg-gated variants like Dashboard).
158        assert_eq!(
159            COMMAND_COUNT, 27,
160            "COMMAND_COUNT drifted — update credits.rs"
161        );
162    }
163
164    #[test]
165    fn test_help_brand_contains_version() {
166        let version = env!("CARGO_PKG_VERSION");
167        assert!(
168            HELP_BRAND.contains(version),
169            "HELP_BRAND should contain the package version"
170        );
171        assert!(HELP_BRAND.contains("raps"));
172    }
173
174    #[test]
175    fn test_version_not_empty() {
176        let version = env!("CARGO_PKG_VERSION");
177        assert!(!version.is_empty());
178    }
179
180    #[test]
181    fn test_mcp_tool_count_reasonable() {
182        let count = crate::mcp::tools::TOOLS.len();
183        assert!(count >= 70, "Expected at least 70 MCP tools, got {count}");
184    }
185}