use async_trait::async_trait;
use tracing::warn;
use crate::audit::module::{AuditModule, ModuleContext, ModuleData};
use crate::audit::PerformanceResults;
use crate::audit::PipelineConfig;
use crate::error::Result;
use super::{
analyze_content_weight, analyze_critical_chain, analyze_minification,
analyze_non_composited_animations, analyze_render_blocking, analyze_third_party_attribution,
calculate_performance_score, extract_web_vitals, take_coverage_results, validate_metrics,
};
pub struct PerformanceModule;
#[async_trait]
impl AuditModule for PerformanceModule {
fn id(&self) -> &'static str {
"performance"
}
fn label(&self) -> &'static str {
"Performance"
}
fn is_enabled(&self, cfg: &PipelineConfig) -> bool {
cfg.check_performance
}
async fn collect(&self, ctx: &ModuleContext<'_>) -> Result<ModuleData> {
let page = ctx.page;
let url = ctx.url;
let vitals = match extract_web_vitals(page).await {
Ok(v) => v,
Err(e) => {
warn!("Performance analysis failed: {}", e);
return Ok(ModuleData::None);
}
};
let content_weight = match analyze_content_weight(page).await {
Ok(cw) => Some(cw),
Err(e) => {
warn!("Content-weight analysis failed: {}", e);
None
}
};
let score = calculate_performance_score(&vitals, content_weight.as_ref());
if score.metrics_available == 0 {
warn!(
"Performance analysis produced no measurable Web Vitals; treating as not measured"
);
return Ok(ModuleData::None);
}
let render_blocking = match analyze_render_blocking(page, url).await {
Ok(rb) => Some(rb),
Err(e) => {
warn!("Render-blocking analysis failed: {}", e);
None
}
};
let third_party = match analyze_third_party_attribution(page, url).await {
Ok(tp) => Some(tp),
Err(e) => {
warn!("Third-party attribution analysis failed: {}", e);
None
}
};
let critical_chain = match analyze_critical_chain(page).await {
Ok(cc) => Some(cc),
Err(e) => {
warn!("Critical chain analysis failed: {}", e);
None
}
};
let minification = match analyze_minification(page).await {
Ok(m) => Some(m),
Err(e) => {
warn!("Minification analysis failed: {}", e);
None
}
};
let animations = match analyze_non_composited_animations(page).await {
Ok(a) => Some(a),
Err(e) => {
warn!("Non-composited animation analysis failed: {}", e);
None
}
};
let coverage = match take_coverage_results(page).await {
Ok(c) => Some(c),
Err(e) => {
warn!("Coverage analysis failed: {}", e);
None
}
};
let mut measurement_warnings = validate_metrics(&vitals);
if let Some(cov) = &coverage {
measurement_warnings.extend(cov.measurement_warnings.clone());
}
Ok(ModuleData::Performance(Box::new(PerformanceResults {
vitals,
score,
render_blocking,
content_weight,
third_party,
critical_chain,
minification,
animations,
coverage,
measurement_warnings,
})))
}
}