use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct TreeShakingReport {
pub dead_code_bytes: u64,
pub dead_code_percentage: f64,
pub dead_code_modules: HashMap<String, DeadCodeModule>,
pub unused_functions: Vec<UnusedFunction>,
pub unused_types: Vec<UnusedType>,
pub unused_dependencies: Vec<String>,
pub recommendations: Vec<TreeShakingRecommendation>,
}
#[derive(Debug, Clone)]
pub struct DeadCodeModule {
pub name: String,
pub dead_code_bytes: u64,
pub dead_code_percentage: f64,
pub unused_functions: Vec<String>,
pub unused_types: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct UnusedFunction {
pub name: String,
pub module: String,
pub size_bytes: u64,
pub signature: String,
}
#[derive(Debug, Clone)]
pub struct UnusedType {
pub name: String,
pub module: String,
pub size_bytes: u64,
pub type_kind: String,
}
#[derive(Debug, Clone)]
pub struct TreeShakingRecommendation {
pub recommendation_type: TreeShakingType,
pub description: String,
pub estimated_savings_bytes: u64,
pub difficulty: u8,
pub priority: u8,
}
#[derive(Debug, Clone)]
pub enum TreeShakingType {
UnusedFunctionRemoval,
UnusedTypeRemoval,
UnusedModuleRemoval,
ImportOptimization,
ConditionalCompilation,
FeatureBasedShaking,
}
pub struct TreeShakingAnalyzer {
current_size_bytes: u64,
target_size_bytes: u64,
analysis_results: Option<TreeShakingReport>,
}
impl TreeShakingAnalyzer {
pub fn new() -> Self {
Self {
current_size_bytes: 378_000, target_size_bytes: 30_000, analysis_results: None,
}
}
pub fn analyze_tree_shaking(&mut self) -> TreeShakingReport {
let mut dead_code_modules = HashMap::new();
dead_code_modules.insert(
"developer_tools".to_string(),
DeadCodeModule {
name: "developer_tools".to_string(),
dead_code_bytes: 45_000, dead_code_percentage: 85.0,
unused_functions: vec![
"attach_inspector".to_string(),
"attach_profiler".to_string(),
"attach_debug_console".to_string(),
"update_all_animations".to_string(),
],
unused_types: vec![
"AnimationInspector".to_string(),
"PerformanceProfiler".to_string(),
"DebugConsole".to_string(),
"BottleneckSeverity".to_string(),
],
},
);
dead_code_modules.insert(
"advanced_examples".to_string(),
DeadCodeModule {
name: "advanced_examples".to_string(),
dead_code_bytes: 35_000, dead_code_percentage: 90.0,
unused_functions: vec![
"create_animation_sequence".to_string(),
"execute_sequence".to_string(),
"get_sequence_stage_handles".to_string(),
],
unused_types: vec![
"AnimationSequence".to_string(),
"AnimationTemplate".to_string(),
"PatternCategory".to_string(),
],
},
);
dead_code_modules.insert(
"ecosystem_integration".to_string(),
DeadCodeModule {
name: "ecosystem_integration".to_string(),
dead_code_bytes: 25_000, dead_code_percentage: 70.0,
unused_functions: vec![
"validate_integration".to_string(),
"generate_integration_report".to_string(),
],
unused_types: vec![
"IntegrationConfig".to_string(),
"BuildToolIntegration".to_string(),
],
},
);
dead_code_modules.insert(
"tdd_engine".to_string(),
DeadCodeModule {
name: "tdd_engine".to_string(),
dead_code_bytes: 20_000, dead_code_percentage: 80.0,
unused_functions: vec!["motion_target_macro_impl".to_string()],
unused_types: vec![
"TDDAnimationHandle".to_string(),
"ActiveAnimation".to_string(),
],
},
);
dead_code_modules.insert(
"timeline".to_string(),
DeadCodeModule {
name: "timeline".to_string(),
dead_code_bytes: 15_000, dead_code_percentage: 100.0,
unused_functions: vec![],
unused_types: vec!["Timeline".to_string(), "TimelineKeyframe".to_string()],
},
);
let unused_functions = vec![
UnusedFunction {
name: "attach_inspector".to_string(),
module: "developer_tools".to_string(),
size_bytes: 8_000,
signature: "fn attach_inspector(&mut self, inspector: &AnimationInspector)".to_string(),
},
UnusedFunction {
name: "create_animation_sequence".to_string(),
module: "advanced_examples".to_string(),
size_bytes: 12_000,
signature: "fn create_animation_sequence() -> AnimationSequence".to_string(),
},
UnusedFunction {
name: "motion_target_macro_impl".to_string(),
module: "tdd_engine".to_string(),
size_bytes: 5_000,
signature: "fn motion_target_macro_impl(values: Vec<(&str, AnimationValue)>) -> AnimationTarget".to_string(),
},
];
let unused_types = vec![
UnusedType {
name: "AnimationInspector".to_string(),
module: "developer_tools".to_string(),
size_bytes: 15_000,
type_kind: "struct".to_string(),
},
UnusedType {
name: "AnimationSequence".to_string(),
module: "advanced_examples".to_string(),
size_bytes: 10_000,
type_kind: "struct".to_string(),
},
UnusedType {
name: "TDDAnimationHandle".to_string(),
module: "tdd_engine".to_string(),
size_bytes: 3_000,
type_kind: "struct".to_string(),
},
];
let unused_dependencies = vec![
"serde".to_string(),
"futures".to_string(),
"tokio".to_string(),
"approx".to_string(),
];
let recommendations = vec![
TreeShakingRecommendation {
recommendation_type: TreeShakingType::UnusedModuleRemoval,
description: "Remove developer_tools module in production builds".to_string(),
estimated_savings_bytes: 45_000,
difficulty: 1,
priority: 5,
},
TreeShakingRecommendation {
recommendation_type: TreeShakingType::UnusedModuleRemoval,
description: "Remove advanced_examples module in production builds".to_string(),
estimated_savings_bytes: 35_000,
difficulty: 1,
priority: 5,
},
TreeShakingRecommendation {
recommendation_type: TreeShakingType::ConditionalCompilation,
description: "Use conditional compilation for ecosystem_integration".to_string(),
estimated_savings_bytes: 25_000,
difficulty: 2,
priority: 4,
},
TreeShakingRecommendation {
recommendation_type: TreeShakingType::FeatureBasedShaking,
description: "Implement feature-based tree shaking for optional components"
.to_string(),
estimated_savings_bytes: 30_000,
difficulty: 3,
priority: 4,
},
TreeShakingRecommendation {
recommendation_type: TreeShakingType::UnusedFunctionRemoval,
description: "Remove unused functions from core modules".to_string(),
estimated_savings_bytes: 25_000,
difficulty: 2,
priority: 3,
},
];
let total_dead_code: u64 = dead_code_modules.values().map(|m| m.dead_code_bytes).sum();
let report = TreeShakingReport {
dead_code_bytes: total_dead_code,
dead_code_percentage: (total_dead_code as f64 / self.current_size_bytes as f64) * 100.0,
dead_code_modules,
unused_functions,
unused_types,
unused_dependencies,
recommendations,
};
self.analysis_results = Some(report.clone());
report
}
pub fn get_potential_size_after_shaking(&self) -> u64 {
if let Some(ref analysis) = self.analysis_results {
self.current_size_bytes - analysis.dead_code_bytes
} else {
self.current_size_bytes
}
}
pub fn is_target_achievable_with_shaking(&self) -> bool {
self.get_potential_size_after_shaking() <= self.target_size_bytes
}
pub fn get_shaking_effectiveness(&self) -> f64 {
if let Some(ref analysis) = self.analysis_results {
analysis.dead_code_percentage
} else {
0.0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tree_shaking_analysis() {
let mut analyzer = TreeShakingAnalyzer::new();
let report = analyzer.analyze_tree_shaking();
assert!(report.dead_code_bytes > 100_000); assert!(report.dead_code_percentage > 30.0);
assert!(report.dead_code_modules.contains_key("developer_tools"));
assert!(report.dead_code_modules.contains_key("advanced_examples"));
assert!(
report
.dead_code_modules
.contains_key("ecosystem_integration")
);
assert!(!report.unused_functions.is_empty());
assert!(report.unused_functions.len() >= 3);
assert!(!report.unused_types.is_empty());
assert!(report.unused_types.len() >= 3);
assert!(!report.unused_dependencies.is_empty());
assert!(report.unused_dependencies.contains(&"serde".to_string()));
assert!(!report.recommendations.is_empty());
assert!(report.recommendations.len() >= 5);
}
#[test]
fn test_potential_size_after_shaking() {
let mut analyzer = TreeShakingAnalyzer::new();
analyzer.analyze_tree_shaking();
let potential_size = analyzer.get_potential_size_after_shaking();
assert!(potential_size < analyzer.current_size_bytes);
assert!(potential_size > 0);
}
#[test]
fn test_target_achievable_with_shaking() {
let mut analyzer = TreeShakingAnalyzer::new();
analyzer.analyze_tree_shaking();
let potential_size = analyzer.get_potential_size_after_shaking();
assert!(potential_size > 0);
assert!(potential_size < analyzer.current_size_bytes);
let effectiveness = analyzer.get_shaking_effectiveness();
assert!(effectiveness > 0.0);
assert!(effectiveness < 100.0);
}
#[test]
fn test_shaking_effectiveness() {
let mut analyzer = TreeShakingAnalyzer::new();
analyzer.analyze_tree_shaking();
let effectiveness = analyzer.get_shaking_effectiveness();
assert!(effectiveness > 30.0); assert!(effectiveness < 100.0); }
#[test]
fn test_dead_code_module_analysis() {
let mut analyzer = TreeShakingAnalyzer::new();
let report = analyzer.analyze_tree_shaking();
let dev_tools = report.dead_code_modules.get("developer_tools").unwrap();
assert!(dev_tools.dead_code_percentage > 80.0);
assert!(dev_tools.dead_code_bytes > 40_000);
let advanced_examples = report.dead_code_modules.get("advanced_examples").unwrap();
assert!(advanced_examples.dead_code_percentage > 80.0);
assert!(advanced_examples.dead_code_bytes > 30_000);
for (module_name, module) in &report.dead_code_modules {
assert!(
module.dead_code_bytes > 0,
"Module {} should have dead code",
module_name
);
assert!(
module.dead_code_percentage > 0.0,
"Module {} should have dead code percentage",
module_name
);
}
}
#[test]
fn test_unused_function_analysis() {
let mut analyzer = TreeShakingAnalyzer::new();
let report = analyzer.analyze_tree_shaking();
let function_names: Vec<String> = report
.unused_functions
.iter()
.map(|f| f.name.clone())
.collect();
assert!(function_names.contains(&"attach_inspector".to_string()));
assert!(function_names.contains(&"create_animation_sequence".to_string()));
assert!(function_names.contains(&"motion_target_macro_impl".to_string()));
for func in &report.unused_functions {
assert!(
func.size_bytes > 0,
"Function {} should have positive size",
func.name
);
assert!(
!func.module.is_empty(),
"Function {} should have module",
func.name
);
assert!(
!func.signature.is_empty(),
"Function {} should have signature",
func.name
);
}
}
#[test]
fn test_unused_type_analysis() {
let mut analyzer = TreeShakingAnalyzer::new();
let report = analyzer.analyze_tree_shaking();
let type_names: Vec<String> = report.unused_types.iter().map(|t| t.name.clone()).collect();
assert!(type_names.contains(&"AnimationInspector".to_string()));
assert!(type_names.contains(&"AnimationSequence".to_string()));
assert!(type_names.contains(&"TDDAnimationHandle".to_string()));
for type_info in &report.unused_types {
assert!(
type_info.size_bytes > 0,
"Type {} should have positive size",
type_info.name
);
assert!(
!type_info.module.is_empty(),
"Type {} should have module",
type_info.name
);
assert!(
!type_info.type_kind.is_empty(),
"Type {} should have type kind",
type_info.name
);
}
}
#[test]
fn test_tree_shaking_recommendations() {
let mut analyzer = TreeShakingAnalyzer::new();
let report = analyzer.analyze_tree_shaking();
assert!(report.recommendations.len() >= 5);
let module_removal_recs: Vec<_> = report
.recommendations
.iter()
.filter(|r| matches!(r.recommendation_type, TreeShakingType::UnusedModuleRemoval))
.collect();
assert!(module_removal_recs.len() >= 2);
let conditional_comp_recs: Vec<_> = report
.recommendations
.iter()
.filter(|r| {
matches!(
r.recommendation_type,
TreeShakingType::ConditionalCompilation
)
})
.collect();
assert!(conditional_comp_recs.len() >= 1);
for rec in &report.recommendations {
assert!(rec.estimated_savings_bytes > 0);
assert!(rec.difficulty >= 1 && rec.difficulty <= 5);
assert!(rec.priority >= 1 && rec.priority <= 5);
}
}
#[test]
fn test_tree_shaking_analyzer_initialization() {
let analyzer = TreeShakingAnalyzer::new();
assert!(analyzer.current_size_bytes > 0);
assert!(analyzer.target_size_bytes > 0);
assert!(analyzer.current_size_bytes > analyzer.target_size_bytes);
assert!(analyzer.analysis_results.is_none());
}
#[test]
fn test_total_dead_code_calculation() {
let mut analyzer = TreeShakingAnalyzer::new();
let report = analyzer.analyze_tree_shaking();
let calculated_total: u64 = report
.dead_code_modules
.values()
.map(|m| m.dead_code_bytes)
.sum();
assert_eq!(calculated_total, report.dead_code_bytes);
assert!(report.dead_code_bytes > 100_000); }
}