ggen_cli_lib/cmds/audit/
performance.rs

1//! Performance analysis and optimization tools.
2//!
3//! This module provides functionality to analyze code performance characteristics,
4//! benchmark execution times, and identify optimization opportunities. It integrates
5//! with cargo-make to perform comprehensive performance analysis.
6//!
7//! # Examples
8//!
9//! ```bash
10//! ggen audit performance analyze --path ./src --benchmark
11//! ggen audit performance benchmark --iterations 1000
12//! ggen audit performance optimize --aggressive
13//! ```
14//!
15//! # Errors
16//!
17//! Returns errors if the underlying cargo-make commands fail or if
18//! the specified paths don't exist.
19
20use clap::{Args, Subcommand};
21use ggen_utils::error::Result;
22// CLI output only - no library logging
23
24#[derive(Args, Debug)]
25pub struct PerformanceArgs {
26    #[command(subcommand)]
27    pub action: PerformanceAction,
28}
29
30#[derive(Subcommand, Debug)]
31pub enum PerformanceAction {
32    /// Benchmark performance characteristics
33    Benchmark(BenchmarkArgs),
34
35    /// Profile memory usage
36    Memory(MemoryArgs),
37
38    /// Check performance SLOs
39    Slo(SloArgs),
40}
41
42#[derive(Args, Debug)]
43pub struct BenchmarkArgs {
44    /// Number of iterations [default: 100]
45    #[arg(long, default_value = "100")]
46    pub iterations: usize,
47
48    /// Output in JSON format
49    #[arg(long)]
50    pub json: bool,
51
52    /// Show detailed metrics
53    #[arg(long)]
54    pub verbose: bool,
55}
56
57#[derive(Args, Debug)]
58pub struct MemoryArgs {
59    /// Memory limit in MB [default: 100]
60    #[arg(long, default_value = "100")]
61    pub limit: usize,
62
63    /// Output in JSON format
64    #[arg(long)]
65    pub json: bool,
66
67    /// Show memory allocation details
68    #[arg(long)]
69    pub detailed: bool,
70}
71
72#[derive(Args, Debug)]
73pub struct SloArgs {
74    /// Check specific SLO [default: all]
75    #[arg(long, default_value = "all")]
76    pub slo: String,
77
78    /// Output in JSON format
79    #[arg(long)]
80    pub json: bool,
81
82    /// Show detailed SLO information
83    #[arg(long)]
84    pub verbose: bool,
85}
86
87pub async fn run(args: &PerformanceArgs) -> Result<()> {
88    match &args.action {
89        PerformanceAction::Benchmark(benchmark_args) => run_benchmark(benchmark_args).await,
90        PerformanceAction::Memory(memory_args) => check_memory(memory_args).await,
91        PerformanceAction::Slo(slo_args) => check_slo(slo_args).await,
92    }
93}
94
95/// Validate iterations count
96fn validate_iterations(iterations: usize) -> Result<()> {
97    if iterations == 0 {
98        return Err(ggen_utils::error::Error::new(
99            "Iterations must be greater than 0",
100        ));
101    }
102
103    if iterations > 10000 {
104        return Err(ggen_utils::error::Error::new(
105            "Iterations too high (max 10000)",
106        ));
107    }
108
109    Ok(())
110}
111
112/// Validate memory limit
113fn validate_memory_limit(limit: usize) -> Result<()> {
114    if limit == 0 {
115        return Err(ggen_utils::error::Error::new(
116            "Memory limit must be greater than 0",
117        ));
118    }
119
120    if limit > 10000 {
121        return Err(ggen_utils::error::Error::new(
122            "Memory limit too high (max 10000 MB)",
123        ));
124    }
125
126    Ok(())
127}
128
129/// Validate SLO name
130fn validate_slo_name(slo: &str) -> Result<()> {
131    if slo.trim().is_empty() {
132        return Err(ggen_utils::error::Error::new("SLO name cannot be empty"));
133    }
134
135    if slo.len() > 100 {
136        return Err(ggen_utils::error::Error::new(
137            "SLO name too long (max 100 characters)",
138        ));
139    }
140
141    // Validate SLO name format (basic pattern check)
142    if !slo
143        .chars()
144        .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
145    {
146        return Err(ggen_utils::error::Error::new(
147            "Invalid SLO name format: only alphanumeric characters, dashes, and underscores allowed",
148        ));
149    }
150
151    Ok(())
152}
153
154async fn run_benchmark(args: &BenchmarkArgs) -> Result<()> {
155    // Validate input
156    validate_iterations(args.iterations)?;
157
158    println!("⚡ Running performance benchmarks");
159
160    let mut cmd = std::process::Command::new("cargo");
161    cmd.args(["make", "bench"]);
162
163    cmd.arg("--iterations").arg(args.iterations.to_string());
164
165    if args.json {
166        cmd.arg("--json");
167    }
168
169    if args.verbose {
170        cmd.arg("--verbose");
171    }
172
173    let output = cmd.output()?;
174
175    if !output.status.success() {
176        let stderr = String::from_utf8_lossy(&output.stderr);
177        return Err(ggen_utils::error::Error::new(&format!(
178            "Benchmark failed: {}",
179            stderr
180        )));
181    }
182
183    let stdout = String::from_utf8_lossy(&output.stdout);
184    println!("{}", stdout);
185    Ok(())
186}
187
188async fn check_memory(args: &MemoryArgs) -> Result<()> {
189    // Validate input
190    validate_memory_limit(args.limit)?;
191
192    println!("💾 Checking memory usage");
193
194    let mut cmd = std::process::Command::new("cargo");
195    cmd.args(["make", "profile"]);
196
197    cmd.arg("--memory-limit").arg(args.limit.to_string());
198
199    if args.json {
200        cmd.arg("--json");
201    }
202
203    if args.detailed {
204        cmd.arg("--detailed");
205    }
206
207    let output = cmd.output()?;
208
209    if !output.status.success() {
210        let stderr = String::from_utf8_lossy(&output.stderr);
211        return Err(ggen_utils::error::Error::new(&format!(
212            "Memory check failed: {}",
213            stderr
214        )));
215    }
216
217    let stdout = String::from_utf8_lossy(&output.stdout);
218    println!("{}", stdout);
219    Ok(())
220}
221
222async fn check_slo(args: &SloArgs) -> Result<()> {
223    // Validate input
224    validate_slo_name(&args.slo)?;
225
226    println!("📊 Checking performance SLOs");
227
228    let mut cmd = std::process::Command::new("cargo");
229    cmd.args(["make", "slo-check"]);
230
231    cmd.arg("--slo").arg(&args.slo);
232
233    if args.json {
234        cmd.arg("--json");
235    }
236
237    if args.verbose {
238        cmd.arg("--verbose");
239    }
240
241    let output = cmd.output()?;
242
243    if !output.status.success() {
244        let stderr = String::from_utf8_lossy(&output.stderr);
245        return Err(ggen_utils::error::Error::new(&format!(
246            "SLO check failed: {}",
247            stderr
248        )));
249    }
250
251    let stdout = String::from_utf8_lossy(&output.stdout);
252    println!("{}", stdout);
253    Ok(())
254}