ggen_cli_lib/cmds/audit/
performance.rs1use clap::{Args, Subcommand};
21use ggen_utils::error::Result;
22#[derive(Args, Debug)]
25pub struct PerformanceArgs {
26 #[command(subcommand)]
27 pub action: PerformanceAction,
28}
29
30#[derive(Subcommand, Debug)]
31pub enum PerformanceAction {
32 Benchmark(BenchmarkArgs),
34
35 Memory(MemoryArgs),
37
38 Slo(SloArgs),
40}
41
42#[derive(Args, Debug)]
43pub struct BenchmarkArgs {
44 #[arg(long, default_value = "100")]
46 pub iterations: usize,
47
48 #[arg(long)]
50 pub json: bool,
51
52 #[arg(long)]
54 pub verbose: bool,
55}
56
57#[derive(Args, Debug)]
58pub struct MemoryArgs {
59 #[arg(long, default_value = "100")]
61 pub limit: usize,
62
63 #[arg(long)]
65 pub json: bool,
66
67 #[arg(long)]
69 pub detailed: bool,
70}
71
72#[derive(Args, Debug)]
73pub struct SloArgs {
74 #[arg(long, default_value = "all")]
76 pub slo: String,
77
78 #[arg(long)]
80 pub json: bool,
81
82 #[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
95fn 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
112fn 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
129fn 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 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_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_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_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}