use super::component::WasmComponent;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::fmt;
pub struct PortabilityAnalyzer {
config: AnalysisConfig,
compatibility_matrix: CompatibilityMatrix,
platform_requirements: HashMap<String, PlatformRequirements>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PortabilityScore {
pub overall_score: f64,
pub platform_scores: HashMap<String, f64>,
pub feature_scores: HashMap<String, f64>,
pub api_compatibility: f64,
pub size_efficiency: f64,
pub performance_portability: f64,
pub security_compliance: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PortabilityReport {
pub component_info: ComponentInfo,
pub score: PortabilityScore,
pub issues: Vec<CompatibilityIssue>,
pub recommendations: Vec<Recommendation>,
pub platform_support: HashMap<String, PlatformSupport>,
pub feature_usage: FeatureUsage,
pub size_analysis: SizeAnalysis,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalysisConfig {
pub target_platforms: Vec<String>,
pub check_api_compatibility: bool,
pub check_size_constraints: bool,
pub check_performance: bool,
pub check_security: bool,
pub strict_mode: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentInfo {
pub name: String,
pub version: String,
pub size: usize,
pub exports_count: usize,
pub imports_count: usize,
pub features: HashSet<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompatibilityIssue {
pub severity: IssueSeverity,
pub category: IssueCategory,
pub affected_platforms: Vec<String>,
pub description: String,
pub fix_suggestion: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum IssueSeverity {
Info,
Warning,
Error,
Critical,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum IssueCategory {
ApiIncompatibility,
FeatureNotSupported,
SizeConstraint,
Performance,
Security,
Configuration,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Recommendation {
pub priority: RecommendationPriority,
pub title: String,
pub description: String,
pub impact: f64,
pub platforms: Vec<String>,
}
impl fmt::Display for Recommendation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.title)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum RecommendationPriority {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlatformSupport {
pub platform: String,
pub support_level: SupportLevel,
pub compatibility_score: f64,
pub required_modifications: Vec<String>,
pub runtime_requirements: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SupportLevel {
Full,
Partial,
Limited,
None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeatureUsage {
pub core_features: HashSet<String>,
pub proposal_features: HashSet<String>,
pub platform_specific: HashMap<String, HashSet<String>>,
pub compatibility: HashMap<String, Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SizeAnalysis {
pub total_size: usize,
pub code_size: usize,
pub data_size: usize,
pub custom_sections_size: usize,
pub section_sizes: HashMap<String, usize>,
pub platform_limits: HashMap<String, usize>,
}
#[derive(Debug, Clone)]
struct PlatformRequirements {
required_features: HashSet<String>,
optional_features: HashSet<String>,
incompatible_features: HashSet<String>,
size_limit: Option<usize>,
_api_requirements: HashSet<String>,
}
struct CompatibilityMatrix {
support: HashMap<String, HashMap<String, bool>>,
}
impl Default for AnalysisConfig {
fn default() -> Self {
Self {
target_platforms: vec![
"cloudflare-workers".to_string(),
"fastly-compute".to_string(),
"aws-lambda".to_string(),
"browser".to_string(),
"nodejs".to_string(),
"wasmtime".to_string(),
],
check_api_compatibility: true,
check_size_constraints: true,
check_performance: true,
check_security: true,
strict_mode: false,
}
}
}
impl Default for PortabilityAnalyzer {
fn default() -> Self {
Self::new()
}
}
impl PortabilityAnalyzer {
pub fn new() -> Self {
Self {
config: AnalysisConfig::default(),
compatibility_matrix: Self::build_compatibility_matrix(),
platform_requirements: Self::build_platform_requirements(),
}
}
pub fn new_with_config(config: AnalysisConfig) -> Self {
Self {
config,
compatibility_matrix: Self::build_compatibility_matrix(),
platform_requirements: Self::build_platform_requirements(),
}
}
pub fn analyze(&self, component: &WasmComponent) -> Result<PortabilityReport> {
let component_info = self.extract_component_info(component)?;
let score = self.calculate_scores(&component_info)?;
let issues = self.find_issues(&component_info)?;
let recommendations = self.generate_recommendations(&component_info, &issues)?;
let platform_support = self.analyze_platform_support(&component_info)?;
let feature_usage = self.analyze_feature_usage(&component_info)?;
let size_analysis = self.analyze_size(component)?;
Ok(PortabilityReport {
component_info,
score,
issues,
recommendations,
platform_support,
feature_usage,
size_analysis,
})
}
fn extract_component_info(&self, component: &WasmComponent) -> Result<ComponentInfo> {
let mut features = HashSet::new();
if component.bytecode.len() > 1024 * 100 {
features.insert("large-module".to_string());
}
Ok(ComponentInfo {
name: component.name.clone(),
version: component.version.clone(),
size: component.bytecode.len(),
exports_count: component.exports.len(),
imports_count: component.imports.len(),
features,
})
}
fn calculate_scores(&self, info: &ComponentInfo) -> Result<PortabilityScore> {
let mut platform_scores = HashMap::new();
for platform in &self.config.target_platforms {
let score = self.calculate_platform_score(info, platform)?;
platform_scores.insert(platform.clone(), score);
}
let feature_scores = self.calculate_feature_scores(info)?;
let api_compatibility = self.calculate_api_compatibility(info)?;
let size_efficiency = self.calculate_size_efficiency(info)?;
let performance_portability = self.calculate_performance_portability(info)?;
let security_compliance = self.calculate_security_compliance(info)?;
let overall_score = Self::calculate_overall_score(
&platform_scores,
&feature_scores,
api_compatibility,
size_efficiency,
performance_portability,
security_compliance,
);
Ok(PortabilityScore {
overall_score,
platform_scores,
feature_scores,
api_compatibility,
size_efficiency,
performance_portability,
security_compliance,
})
}
fn calculate_platform_score(&self, info: &ComponentInfo, platform: &str) -> Result<f64> {
let requirements = self.platform_requirements.get(platform);
if let Some(reqs) = requirements {
let mut score = 1.0;
if let Some(limit) = reqs.size_limit {
if info.size > limit {
score *= 0.5; }
}
for feature in &info.features {
if reqs.incompatible_features.contains(feature) {
score *= 0.0; } else if !reqs.required_features.contains(feature)
&& !reqs.optional_features.contains(feature)
{
score *= 0.8; }
}
Ok(score)
} else {
Ok(0.5) }
}
fn calculate_feature_scores(&self, info: &ComponentInfo) -> Result<HashMap<String, f64>> {
let mut scores = HashMap::new();
for feature in &info.features {
let mut support_count = 0;
let total_platforms = self.config.target_platforms.len();
for platform in &self.config.target_platforms {
if let Some(platform_features) = self.compatibility_matrix.support.get(platform) {
if platform_features.get(feature).copied().unwrap_or(false) {
support_count += 1;
}
}
}
let score = f64::from(support_count) / total_platforms as f64;
scores.insert(feature.clone(), score);
}
Ok(scores)
}
fn calculate_api_compatibility(&self, _info: &ComponentInfo) -> Result<f64> {
Ok(0.9)
}
fn calculate_size_efficiency(&self, info: &ComponentInfo) -> Result<f64> {
let size_kb = info.size as f64 / 1024.0;
if size_kb < 50.0 {
Ok(1.0)
} else if size_kb < 100.0 {
Ok(0.9)
} else if size_kb < 500.0 {
Ok(0.7)
} else if size_kb < 1000.0 {
Ok(0.5)
} else {
Ok(0.3)
}
}
fn calculate_performance_portability(&self, _info: &ComponentInfo) -> Result<f64> {
Ok(0.85)
}
fn calculate_security_compliance(&self, _info: &ComponentInfo) -> Result<f64> {
Ok(0.95)
}
fn calculate_overall_score(
platform_scores: &HashMap<String, f64>,
feature_scores: &HashMap<String, f64>,
api_compatibility: f64,
size_efficiency: f64,
performance_portability: f64,
security_compliance: f64,
) -> f64 {
let platform_avg = if platform_scores.is_empty() {
0.0
} else {
platform_scores.values().sum::<f64>() / platform_scores.len() as f64
};
let feature_avg = if feature_scores.is_empty() {
1.0
} else {
feature_scores.values().sum::<f64>() / feature_scores.len() as f64
};
platform_avg * 0.3
+ feature_avg * 0.2
+ api_compatibility * 0.2
+ size_efficiency * 0.1
+ performance_portability * 0.1
+ security_compliance * 0.1
}
fn find_issues(&self, info: &ComponentInfo) -> Result<Vec<CompatibilityIssue>> {
let mut issues = Vec::new();
for platform in &self.config.target_platforms {
if let Some(reqs) = self.platform_requirements.get(platform) {
if let Some(limit) = reqs.size_limit {
if info.size > limit {
issues.push(CompatibilityIssue {
severity: IssueSeverity::Warning,
category: IssueCategory::SizeConstraint,
affected_platforms: vec![platform.clone()],
description: format!(
"Component size ({} KB) exceeds {} platform limit ({} KB)",
info.size / 1024,
platform,
limit / 1024
),
fix_suggestion: Some(
"Consider optimizing component size or splitting functionality"
.to_string(),
),
});
}
}
}
}
Ok(issues)
}
fn generate_recommendations(
&self,
info: &ComponentInfo,
issues: &[CompatibilityIssue],
) -> Result<Vec<Recommendation>> {
let mut recommendations = Vec::new();
if info.size > 100 * 1024 {
recommendations.push(Recommendation {
priority: RecommendationPriority::High,
title: "Optimize component size".to_string(),
description: "Component size can be reduced through optimization techniques"
.to_string(),
impact: 0.2,
platforms: self.config.target_platforms.clone(),
});
}
for issue in issues {
if issue.category == IssueCategory::FeatureNotSupported {
recommendations.push(Recommendation {
priority: RecommendationPriority::Critical,
title: "Remove incompatible features".to_string(),
description: issue.description.clone(),
impact: 0.3,
platforms: issue.affected_platforms.clone(),
});
}
}
Ok(recommendations)
}
fn analyze_platform_support(
&self,
info: &ComponentInfo,
) -> Result<HashMap<String, PlatformSupport>> {
let mut support = HashMap::new();
for platform in &self.config.target_platforms {
let score = self.calculate_platform_score(info, platform)?;
let support_level = if score >= 0.9 {
SupportLevel::Full
} else if score >= 0.7 {
SupportLevel::Partial
} else if score >= 0.3 {
SupportLevel::Limited
} else {
SupportLevel::None
};
support.insert(
platform.clone(),
PlatformSupport {
platform: platform.clone(),
support_level,
compatibility_score: score,
required_modifications: Vec::new(),
runtime_requirements: None,
},
);
}
Ok(support)
}
fn analyze_feature_usage(&self, info: &ComponentInfo) -> Result<FeatureUsage> {
Ok(FeatureUsage {
core_features: info.features.clone(),
proposal_features: HashSet::new(),
platform_specific: HashMap::new(),
compatibility: HashMap::new(),
})
}
fn analyze_size(&self, component: &WasmComponent) -> Result<SizeAnalysis> {
let mut section_sizes = HashMap::new();
for (name, data) in &component.custom_sections {
section_sizes.insert(name.clone(), data.len());
}
let custom_sections_size: usize = component
.custom_sections
.values()
.map(std::vec::Vec::len)
.sum();
Ok(SizeAnalysis {
total_size: component.bytecode.len(),
code_size: component.bytecode.len() - custom_sections_size,
data_size: 0,
custom_sections_size,
section_sizes,
platform_limits: self.get_platform_limits(),
})
}
fn get_platform_limits(&self) -> HashMap<String, usize> {
let mut limits = HashMap::new();
limits.insert("cloudflare-workers".to_string(), 10 * 1024 * 1024); limits.insert("fastly-compute".to_string(), 50 * 1024 * 1024); limits.insert("aws-lambda".to_string(), 250 * 1024 * 1024); limits.insert("browser".to_string(), 100 * 1024 * 1024); limits
}
fn build_compatibility_matrix() -> CompatibilityMatrix {
let mut support = HashMap::new();
let mut cloudflare = HashMap::new();
cloudflare.insert("simd".to_string(), false);
cloudflare.insert("threads".to_string(), false);
cloudflare.insert("bulk-memory".to_string(), true);
cloudflare.insert("reference-types".to_string(), true);
support.insert("cloudflare-workers".to_string(), cloudflare);
let mut browser = HashMap::new();
browser.insert("simd".to_string(), true);
browser.insert("threads".to_string(), true);
browser.insert("bulk-memory".to_string(), true);
browser.insert("reference-types".to_string(), true);
support.insert("browser".to_string(), browser);
CompatibilityMatrix { support }
}
fn build_platform_requirements() -> HashMap<String, PlatformRequirements> {
let mut requirements = HashMap::new();
requirements.insert(
"cloudflare-workers".to_string(),
PlatformRequirements {
required_features: HashSet::new(),
optional_features: HashSet::from([
"bulk-memory".to_string(),
"reference-types".to_string(),
]),
incompatible_features: HashSet::from(["threads".to_string()]),
size_limit: Some(10 * 1024 * 1024),
_api_requirements: HashSet::new(),
},
);
requirements.insert(
"browser".to_string(),
PlatformRequirements {
required_features: HashSet::new(),
optional_features: HashSet::from(["simd".to_string(), "threads".to_string()]),
incompatible_features: HashSet::new(),
size_limit: Some(100 * 1024 * 1024),
_api_requirements: HashSet::new(),
},
);
requirements
}
}
#[cfg(test)]
#[path = "portability_tests.rs"]
mod tests;
#[cfg(test)]
#[path = "portability_additional_tests.rs"]
mod additional_portability_tests;
#[cfg(test)]
#[path = "portability_prop_tests.rs"]
mod property_tests_portability;