use std::{
time::{Duration, Instant},
sync::mpsc::{self, Sender, Receiver},
thread,
};
use console::{Term, style};
use crate::ui::{info, success, error, create_build_progress, create_dependency_progress};
use crate::build::{BuildSystem, BuildExecutor, ExecutionRequest};
use crate::maven::core::MavenBuildExecutor;
use crate::gradle::core::GradleExecutor;
use anyhow::Result;
use chrono;
#[derive(Debug, Clone)]
pub struct BuildMetrics {
pub start_time: Instant,
pub phase_times: Vec<(String, Duration)>,
pub current_phase: Option<String>,
pub total_dependencies: usize,
pub processed_dependencies: usize,
pub compilation_files: usize,
pub test_files: usize,
pub memory_usage_mb: u64,
pub cpu_usage_percent: f32,
}
impl BuildMetrics {
pub fn new() -> Self {
Self {
start_time: Instant::now(),
phase_times: Vec::new(),
current_phase: None,
total_dependencies: 0,
processed_dependencies: 0,
compilation_files: 0,
test_files: 0,
memory_usage_mb: 0,
cpu_usage_percent: 0.0,
}
}
pub fn elapsed(&self) -> Duration {
self.start_time.elapsed()
}
pub fn add_phase_time(&mut self, phase: String, duration: Duration) {
self.phase_times.push((phase, duration));
}
pub fn update_progress(&mut self, processed: usize, total: usize) {
self.processed_dependencies = processed;
self.total_dependencies = total;
}
}
pub fn run_interactive(verbose: bool, monitor: bool, dashboard: bool) -> Result<()> {
info("🚀 Starting interactive build mode");
let base_dir = std::env::current_dir()?;
let build_system = BuildSystem::detect(&base_dir)
.ok_or_else(|| anyhow::anyhow!("No build system detected"))?;
info(&format!("📦 Detected build system: {}",
match build_system {
BuildSystem::Maven => "Maven",
BuildSystem::Gradle => "Gradle",
}
));
let term = Term::stdout();
term.clear_line()?;
if dashboard {
run_dashboard(build_system, verbose)?;
} else {
run_interactive_build(build_system, verbose, monitor)?;
}
Ok(())
}
fn run_interactive_build(build_system: BuildSystem, _verbose: bool, _monitor: bool) -> Result<()> {
let (metrics_tx, metrics_rx) = mpsc::channel();
let (progress_tx, _progress_rx) = mpsc::channel();
let progress_clone = progress_tx.clone();
let handle = thread::spawn(move || {
monitor_build_progress(metrics_rx, progress_tx);
});
let request = ExecutionRequest {
base_directory: std::env::current_dir()?,
goals: vec!["compile".to_string()],
system_properties: std::collections::HashMap::new(),
show_errors: true,
offline: false,
};
let executor: Box<dyn BuildExecutor> = match build_system {
BuildSystem::Maven => Box::new(MavenBuildExecutor::new()),
BuildSystem::Gradle => Box::new(GradleExecutor::new()),
};
metrics_tx.send(BuildMetrics::new())?;
simulate_progress_updates(progress_clone)?;
let _result = executor.execute(request);
handle.join().unwrap();
success("✅ Build completed successfully!");
Ok(())
}
fn run_dashboard(build_system: BuildSystem, _verbose: bool) -> Result<()> {
info("📊 Starting build dashboard");
let term = Term::stdout();
let mut metrics = BuildMetrics::new();
let main_progress = create_build_progress();
let dependency_progress = create_dependency_progress(100);
println!("\n{} jbuild Build Dashboard {}\n",
style("=".repeat(10)).blue().bold(),
style("=".repeat(10)).blue().bold()
);
let mut running = true;
let start_time = Instant::now();
while running {
term.clear_line()?;
metrics.processed_dependencies = (metrics.processed_dependencies + 1) % 100;
metrics.total_dependencies = 100;
metrics.compilation_files = (metrics.compilation_files + 2) % 50;
metrics.test_files = (metrics.test_files + 1) % 20;
display_dashboard(&metrics, &start_time, build_system)?;
thread::sleep(Duration::from_millis(100));
if start_time.elapsed() > Duration::from_secs(10) {
running = false;
}
}
main_progress.finish();
dependency_progress.finish();
success("🎉 Dashboard session completed!");
Ok(())
}
pub fn display_dashboard(metrics: &BuildMetrics, start_time: &Instant, build_system: BuildSystem) -> Result<()> {
let elapsed = start_time.elapsed();
let elapsed_secs = elapsed.as_secs();
let elapsed_mins = elapsed_secs / 60;
let remaining_secs = elapsed_secs % 60;
println!("{} Build Status {}", style("▶").green(), style(format!("[{elapsed_mins:02}:{remaining_secs:02}]")).cyan());
println!("📦 System: {}",
match build_system {
BuildSystem::Maven => "Maven",
BuildSystem::Gradle => "Gradle",
}
);
println!("⚡ Dependencies: {}/{}", metrics.processed_dependencies, metrics.total_dependencies);
println!("📄 Compiling: {} files", metrics.compilation_files);
println!("🧪 Testing: {} files", metrics.test_files);
let memory_mb = 512 + (metrics.processed_dependencies as f64 * 0.1) as u64;
let cpu_percent = 25.0 + (metrics.processed_dependencies as f64 * 0.5);
println!("💾 Memory: {} MB", memory_mb);
println!("🖥️ CPU: {:.1}%", cpu_percent);
let throughput = metrics.processed_dependencies as f64 / elapsed_secs.max(1) as f64;
println!("⚡ Throughput: {:.1} deps/sec", throughput);
println!();
Ok(())
}
fn monitor_build_progress(metrics_rx: Receiver<BuildMetrics>, progress_tx: Sender<String>) {
while let Ok(metrics) = metrics_rx.recv() {
if let Some(ref phase) = metrics.current_phase {
progress_tx.send(format!("🔄 {}: {}%", phase, metrics.processed_dependencies)).unwrap();
}
}
}
pub fn simulate_progress_updates(progress_tx: Sender<String>) -> Result<()> {
for i in 0..100 {
progress_tx.send(format!("Processing dependency {}/100", i + 1))?;
thread::sleep(Duration::from_millis(50));
}
Ok(())
}
pub fn run_monitor(duration: Option<u64>, realtime: bool) -> Result<()> {
info("🔍 Starting real-time build monitoring");
let term = Term::stdout();
let mut metrics = BuildMetrics::new();
let mut running = true;
let start_time = Instant::now();
let main_progress = create_build_progress();
let dependency_progress = create_dependency_progress(100);
if realtime {
term.clear_line()?;
println!("{} Real-time Build Monitor {}\n",
style("▶".repeat(3)).green().bold(),
style("◀".repeat(3)).green().bold()
);
}
while running {
metrics.processed_dependencies = (metrics.processed_dependencies + 1) % 100;
metrics.total_dependencies = 100;
metrics.compilation_files = (metrics.compilation_files + 2) % 50;
metrics.test_files = (metrics.test_files + 1) % 20;
metrics.memory_usage_mb = 512 + (metrics.processed_dependencies as f64 * 0.1) as u64;
metrics.cpu_usage_percent = (25.0 + (metrics.processed_dependencies as f64 * 0.5)) as f32;
if realtime {
display_monitor(&metrics, &start_time, realtime)?;
}
if let Some(duration_seconds) = duration {
if start_time.elapsed().as_secs() >= duration_seconds {
running = false;
}
} else if start_time.elapsed() > Duration::from_secs(30) {
running = false;
}
thread::sleep(Duration::from_millis(200));
}
main_progress.finish();
dependency_progress.finish();
success("🎉 Monitoring session completed!");
Ok(())
}
pub fn display_monitor(metrics: &BuildMetrics, start_time: &Instant, _realtime: bool) -> Result<()> {
let elapsed = start_time.elapsed();
let elapsed_secs = elapsed.as_secs();
let elapsed_mins = elapsed_secs / 60;
let remaining_secs = elapsed_secs % 60;
print!("\r");
print!("{} [{elapsed_mins:02}:{remaining_secs:02}] ", style("▶").green());
print!("Deps: {}/{} | ", metrics.processed_dependencies, metrics.total_dependencies);
print!("Files: {}/{} | ", metrics.compilation_files, metrics.test_files);
print!("Mem: {}MB | ", metrics.memory_usage_mb);
print!("CPU: {:.1}%", metrics.cpu_usage_percent);
std::io::Write::flush(&mut std::io::stdout())?;
Ok(())
}
pub fn run_profile(output: Option<std::path::PathBuf>, format: String) -> Result<()> {
info("🔍 Starting build profiling");
let start_time = Instant::now();
let base_dir = std::env::current_dir()?;
let build_system = BuildSystem::detect(&base_dir)
.ok_or_else(|| anyhow::anyhow!("No build system detected"))?;
let request = ExecutionRequest {
base_directory: base_dir,
goals: vec!["compile".to_string()],
system_properties: std::collections::HashMap::new(),
show_errors: true,
offline: false,
};
let executor: Box<dyn BuildExecutor> = match build_system {
BuildSystem::Maven => Box::new(MavenBuildExecutor::new()),
BuildSystem::Gradle => Box::new(GradleExecutor::new()),
};
let _result = executor.execute(request);
let build_time = start_time.elapsed();
generate_profile_report(build_time, &output, &format)?;
success("✅ Build profiling completed");
Ok(())
}
pub fn generate_profile_report(build_time: Duration,
output: &Option<std::path::PathBuf>,
format: &str) -> Result<()> {
let report = ProfileReport {
build_time,
success: true, error_count: 0, phases: vec![
("Dependency Resolution".to_string(), Duration::from_millis(1500)),
("Compilation".to_string(), Duration::from_millis(3000)),
("Testing".to_string(), Duration::from_millis(2000)),
],
timestamp: chrono::Local::now(),
};
match format {
"json" => {
let json = serde_json::to_string_pretty(&report)?;
match output {
Some(path) => std::fs::write(path, json)?,
None => println!("{}", json),
}
}
"markdown" => {
let markdown = generate_markdown_report(&report);
match output {
Some(path) => std::fs::write(path, markdown)?,
None => println!("{}", markdown),
}
}
"text" => {
let text = generate_text_report(&report);
match output {
Some(path) => std::fs::write(path, text)?,
None => println!("{}", text),
}
}
_ => {
error(&format!("Unsupported format: {}", format));
}
}
Ok(())
}
#[derive(Debug, serde::Serialize)]
pub struct ProfileReport {
pub build_time: Duration,
pub success: bool,
pub error_count: usize,
pub phases: Vec<(String, Duration)>,
pub timestamp: chrono::DateTime<chrono::Local>,
}
pub fn generate_markdown_report(report: &ProfileReport) -> String {
let _total_phases = report.phases.len();
format!(
"# Build Profile Report\n\n\
**Timestamp**: {}\n\n\
## Summary\n\n\
- **Build Time**: {:.2}s\n\
- **Status**: {}\n\
- **Errors**: {}\n\n\
## Phase Timing\n\n\
| Phase | Duration | Percentage |\n\
|-------|----------|------------|\n\
{}",
report.timestamp.format("%Y-%m-%d %H:%M:%S"),
report.build_time.as_secs_f64(),
if report.success { "✅ Success" } else { "❌ Failed" },
report.error_count,
report.phases.iter().map(|(phase, duration)| {
let percentage = (duration.as_millis() as f64 / report.build_time.as_millis() as f64) * 100.0;
format!("| {} | {:.2}s | {:.1}% |", phase, duration.as_secs_f64(), percentage)
}).collect::<Vec<_>>().join("\n")
)
}
pub fn generate_text_report(report: &ProfileReport) -> String {
let _total_phases = report.phases.len();
let phase_width = report.phases.iter().map(|(p, _)| p.len()).max().unwrap_or(20);
format!(
"Build Profile Report\n\
===================\n\n\
Timestamp: {}\n\
Build Time: {:.2}s\n\
Status: {}\n\
Errors: {}\n\n\
Phase Timing:\n\
{}\n\
Total: {:.2}s\n",
report.timestamp.format("%Y-%m-%d %H:%M:%S"),
report.build_time.as_secs_f64(),
if report.success { "SUCCESS" } else { "FAILED" },
report.error_count,
report.phases.iter().map(|(phase, duration)| {
let percentage = (duration.as_millis() as f64 / report.build_time.as_millis() as f64) * 100.0;
format!(" {:<width$}: {:.2}s ({:.1}%)", phase, duration.as_secs_f64(), percentage, width = phase_width)
}).collect::<Vec<_>>().join("\n"),
report.build_time.as_secs_f64()
)
}