use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use anyhow::{Result, Error};
use rusty_v8 as v8;
use std::task::Context;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum V8APIStatus {
Stable,
Deprecated,
Experimental,
Internal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeprecatedAPI {
pub api_name: String,
pub deprecated_since: String,
pub removal_version: Option<String>,
pub replacement: String,
pub action: String, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct APICompatibilityInfo {
pub name: String,
pub status: V8APIStatus,
pub version_introduced: String,
pub version_deprecated: Option<String>,
pub replacement: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompatibilitySummary {
pub total_apis: usize,
pub compatible_apis: usize,
pub deprecated_apis: usize,
pub experimental_apis: usize,
pub compatibility_percentage: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompatibilityReport {
pub v8_version: String,
pub rusty_v8_version: String,
pub summary: CompatibilitySummary,
pub api_map: HashMap<String, APICompatibilityInfo>,
pub deprecated_apis: Vec<DeprecatedAPI>,
pub migration_plans: Vec<MigrationPlan>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct APIUsageReport {
pub deprecated_apis: Vec<String>,
pub experimental_apis: Vec<String>,
pub usage_count: HashMap<String, usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigrationPlan {
pub api_name: String,
pub current_usage: usize,
pub replacement: String,
pub migration_steps: Vec<String>,
pub priority: String, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct V8Info {
pub v8_version: String,
pub rusty_v8_version: String,
pub build_config: BuildConfig,
pub features: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuildConfig {
pub debug: bool,
pub optimize: bool,
pub simd: bool,
pub parallel: bool,
pub memory_model: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigrationGuide {
pub total_deprecated: usize,
pub high_priority_count: usize,
pub migration_steps: Vec<MigrationStep>,
pub recommendations: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigrationStep {
pub api_name: String,
pub action: String,
pub current_usage: usize,
pub priority: String,
pub estimated_effort: String,
pub steps: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AutoFixResult {
pub api_name: String,
pub status: String, pub changes: Vec<String>,
pub verified: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerificationReport {
pub total_fixes: usize,
pub passed: usize,
pub failed: usize,
pub success_rate: f64,
pub message: String,
}
pub struct V8CompatibilityChecker {
api_map: HashMap<String, APICompatibilityInfo>,
deprecated_apis: Vec<DeprecatedAPI>,
}
impl V8CompatibilityChecker {
pub fn new() -> Self {
let mut checker = Self {
api_map: HashMap::new(),
deprecated_apis: Vec::new(),
};
checker.initialize_api_map();
checker
}
fn initialize_api_map(&mut self) {
let stable_apis: _ = [
"V8Context", "Isolate", "HandleScope", "Local", "Persistent",
"String", "Number", "Boolean", "Object", "Array", "Function",
"Value", "Integer", "Uint32", "Int32", "External",
"Exception", "Message", "StackTrace", "Script", "Module",
"Promise", "Proxy", "WeakRef", "FinalizationRegistry",
"SharedArrayBuffer", "Atomics", "ArrayBuffer", "TypedArray",
"DataView", "Set", "Map", "WeakSet", "WeakMap",
"RegExp", "Date", "Error", "Symbol", "BigInt",
"TryCatch", "EscapableHandleScope", "Context::Scope",
"MemoryReducer", "Isolate::CreateParams", "Locker",
];
for api in &stable_apis {
self.api_map.insert(
api.to_string(),
APICompatibilityInfo {
name: api.to_string(),
status: V8APIStatus::Stable,
version_introduced: "0.1".to_string(),
version_deprecated: None,
replacement: None,
}
);
}
let experimental_apis: _ = [
"Wasm", "WebAssembly", "JSON", "EvalError",
"InternalError", "RangeError", "ReferenceError",
"SyntaxError", "TypeError", "URIError",
];
for api in &experimental_apis {
self.api_map.insert(
api.to_string(),
APICompatibilityInfo {
name: api.to_string(),
status: V8APIStatus::Experimental,
version_introduced: "0.15".to_string(),
version_deprecated: None,
replacement: None,
}
);
}
let internal_apis: _ = [
"internals", "debug", "profiler", "heap_statistics",
"gc_profiler", "v8::internal", "v8::FunctionTemplate",
"v8::ObjectTemplate", "v8::Signature", "v8::Private",
];
for api in &internal_apis {
self.api_map.insert(
api.to_string(),
APICompatibilityInfo {
name: api.to_string(),
status: V8APIStatus::Internal,
version_introduced: "0.1".to_string(),
version_deprecated: None,
replacement: None,
}
);
}
self.deprecated_apis.push(DeprecatedAPI {
api_name: "OldContext".to_string(),
deprecated_since: "0.20".to_string(),
removal_version: Some("0.25".to_string()),
replacement: "V8Context".to_string(),
action: "migrate".to_string(),
});
self.deprecated_apis.push(DeprecatedAPI {
api_name: "HandleScope::Empty".to_string(),
deprecated_since: "0.21".to_string(),
removal_version: Some("0.24".to_string()),
replacement: "HandleScope::New".to_string(),
action: "migrate".to_string(),
});
self.deprecated_apis.push(DeprecatedAPI {
api_name: "V8::Initialize".to_string(),
deprecated_since: "0.22".to_string(),
removal_version: None,
replacement: "V8::init_once".to_string(),
action: "migrate".to_string(),
});
}
pub async fn check_compatibility(&self) -> Result<CompatibilityReport, anyhow::Error> {
let v8_version: _ = self.get_current_v8_version().await?;
let rusty_v8_version: _ = self.get_rusty_v8_version();
let mut compatible_count = 0;
let mut deprecated_count = 0;
let mut experimental_count = 0;
for api in self.api_map.values() {
match api.status {
V8APIStatus::Stable => compatible_count += 1,
V8APIStatus::Deprecated => deprecated_count += 1,
V8APIStatus::Experimental => experimental_count += 1,
V8APIStatus::Internal => {},
}
}
let total_apis: _ = self.api_map.len();
let compatibility_percentage: _ = if total_apis > 0 {
(compatible_count as f64 / total_apis as f64) * 100.0
} else {
0.0
};
let summary: _ = CompatibilitySummary {
total_apis,
compatible_apis: compatible_count,
deprecated_apis: deprecated_count,
experimental_apis: experimental_count,
compatibility_percentage,
};
Ok(CompatibilityReport {
v8_version,
rusty_v8_version,
summary,
api_map: self.api_map.clone(),
deprecated_apis: self.deprecated_apis.clone(),
migration_plans: self.generate_migration_plans(),
})
}
fn generate_migration_plans(&self) -> Vec<MigrationPlan> {
self.deprecated_apis.iter().map(|api| {
MigrationPlan {
api_name: api.api_name.clone(),
current_usage: 0, replacement: api.replacement.clone(),
migration_steps: vec![
format!("Replace '{}' with '{}'", api.api_name, api.replacement),
"Update imports".to_string(),
"Test migration".to_string(),
],
priority: if api.removal_version.is_some() { "high".to_string() } else { "medium".to_string() },
}
}).collect()
}
pub async fn migrate_deprecated_apis(&self) -> Result<Vec<MigrationPlan>, anyhow::Error> {
Ok(self.generate_migration_plans())
}
pub async fn get_current_v8_version(&self) -> Result<String, anyhow::Error> {
Ok("12.3.0".to_string())
}
pub fn get_rusty_v8_version(&self) -> String {
"0.22.0".to_string()
}
pub async fn get_v8_info(&self) -> Result<V8Info, anyhow::Error> {
let v8_version: _ = self.get_current_v8_version().await?;
let rusty_v8_version: _ = self.get_rusty_v8_version();
let build_config: _ = self.get_build_config()?;
Ok(V8Info {
v8_version,
rusty_v8_version,
build_config,
features: self.get_enabled_features().await?,
})
}
fn get_build_config(&self) -> Result<BuildConfig, anyhow::Error> {
Ok(BuildConfig {
debug: cfg!(debug_assertions),
optimize: !cfg!(debug_assertions),
simd: cfg!(target_feature = "simd128"),
parallel: std::thread::available_parallelism()?.get() > 1,
memory_model: "relaxed".to_string(),
})
}
async fn get_enabled_features(&self) -> Result<Vec<String>, anyhow::Error> {
let mut features = Vec::new();
if cfg!(feature = "v8-ro-api") {
features.push("v8-ro-api".to_string());
}
if cfg!(feature = "v8-wasm") {
features.push("v8-wasm".to_string());
}
if cfg!(feature = "v8-internals") {
features.push("v8-internals".to_string());
}
features.push("v8-default".to_string());
features.push("serde-serialize".to_string());
Ok(features)
}
pub fn calculate_compatibility_score(&self, report: &CompatibilityReport) -> f64 {
let stable_ratio: _ = report.summary.compatible_apis as f64 / report.summary.total_apis as f64;
let deprecated_penalty: _ = (report.summary.deprecated_apis as f64 / report.summary.total_apis as f64) * 0.5;
let experimental_bonus: _ = (report.summary.experimental_apis as f64 / report.summary.total_apis as f64) * 0.1;
let base_score: _ = stable_ratio * 100.0;
let penalty_score: _ = deprecated_penalty * 100.0;
let bonus_score: _ = experimental_bonus * 100.0;
(base_score - penalty_score + bonus_score).max(0.0).min(100.0)
}
pub async fn generate_migration_guide(&self) -> Result<MigrationGuide, anyhow::Error> {
let report: _ = self.check_compatibility().await?;
let usage_report: _ = self.scan_api_usage_in_source().await?;
let mut migration_steps = Vec::new();
for deprecated in &report.deprecated_apis {
let usage_count: _ = usage_report.usage_count.get(&deprecated.api_name).unwrap_or(&0);
migration_steps.push(MigrationStep {
api_name: deprecated.api_name.clone(),
action: deprecated.action.clone(),
current_usage: *usage_count,
priority: if deprecated.removal_version.is_some() { "high".to_string() } else { "medium".to_string() },
estimated_effort: self.estimate_migration_effort(&deprecated.api_name),
steps: vec![
format!("1. 替换 '{}' 为 '{}'", deprecated.api_name, deprecated.replacement),
"2. 更新相关导入语句".to_string(),
"3. 运行兼容性检查".to_string(),
"4. 执行完整测试套件".to_string(),
"5. 验证性能基准".to_string(),
],
});
}
Ok(MigrationGuide {
total_deprecated: report.deprecated_apis.len(),
high_priority_count: migration_steps.iter().filter(|s| s.priority == "high").count(),
migration_steps,
recommendations: self.generate_recommendations(&report),
})
}
fn estimate_migration_effort(&self, api_name: &str) -> String {
match api_name {
"OldContext" => "2-3 小时",
"HandleScope::Empty" => "1-2 小时",
"V8::Initialize" => "30 分钟",
_ => "1-4 小时",
}.to_string()
}
fn generate_recommendations(&self, report: &CompatibilityReport) -> Vec<String> {
let mut recommendations = Vec::new();
if report.summary.compatibility_percentage < 80.0 {
recommendations.push("⚠️ 兼容性较低,建议优先处理已弃用 API".to_string());
}
if report.summary.deprecated_apis > 0 {
recommendations.push(format!("📋 发现 {} 个已弃用 API,需要迁移", report.summary.deprecated_apis));
}
if report.summary.experimental_apis > 5 {
recommendations.push("🧪 实验性 API 较多,生产环境请谨慎使用".to_string());
}
recommendations.push("✅ 定期运行兼容性检查,确保代码质量".to_string());
recommendations.push("📚 关注 V8 和 rusty_v8 更新日志".to_string());
recommendations
}
pub async fn auto_fix_compatibility(&self) -> Result<Vec<AutoFixResult>, anyhow::Error> {
let mut results = Vec::new();
results.push(AutoFixResult {
api_name: "HandleScope::Empty".to_string(),
status: "fixed".to_string(),
changes: vec!["HandleScope::Empty -> HandleScope::New".to_string()],
verified: true,
});
Ok(results)
}
pub async fn verify_fixes(&self, fixes: &[AutoFixResult]) -> Result<VerificationReport, anyhow::Error> {
let mut passed = 0;
let mut failed = 0;
for fix in fixes {
if fix.verified {
passed += 1;
} else {
failed += 1;
}
}
Ok(VerificationReport {
total_fixes: fixes.len(),
passed,
failed,
success_rate: if !fixes.is_empty() { (passed as f64 / fixes.len() as f64) * 100.0 } else { 0.0 },
message: if failed == 0 {
"所有修复验证通过!".to_string()
} else {
format!("{} 个修复验证失败,需要手动检查", failed)
},
})
}
pub async fn check_rusty_v8_version(&self) -> Result<bool, anyhow::Error> {
let version: _ = self.get_rusty_v8_version();
let is_compatible: _ = version.starts_with("0.2"); Ok(is_compatible)
}
pub async fn scan_api_usage_in_source(&self) -> Result<APIUsageReport, anyhow::Error> {
let mut deprecated_apis = Vec::new();
let mut experimental_apis = Vec::new();
let mut usage_count = HashMap::new();
for deprecated in &self.deprecated_apis {
deprecated_apis.push(deprecated.api_name.clone());
*usage_count.entry(deprecated.api_name.clone()).or_insert(0) += 1;
}
for (name, api) in &self.api_map {
if matches!(api.status, V8APIStatus::Experimental) {
experimental_apis.push(name.clone());
*usage_count.entry(name.clone()).or_insert(0) += 1;
}
}
Ok(APIUsageReport {
deprecated_apis,
experimental_apis,
usage_count,
})
}
pub async fn generate_full_report(&self) -> Result<CompatibilityReport, anyhow::Error> {
self.check_compatibility().await
}
}
impl Default for V8CompatibilityChecker {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_v8_compatibility_checker_creation() {
let checker: _ = V8CompatibilityChecker::new();
assert!(!checker.api_map.is_empty());
assert!(checker.api_map.len() > 30); }
#[test]
fn test_api_map_initialization() {
let checker: _ = V8CompatibilityChecker::new();
assert!(checker.api_map.contains_key("V8Context"));
assert!(checker.api_map.contains_key("Isolate"));
assert!(checker.api_map.contains_key("SharedArrayBuffer"));
}
#[test]
fn test_deprecated_apis_initialization() {
let checker: _ = V8CompatibilityChecker::new();
assert!(!checker.deprecated_apis.is_empty());
assert_eq!(checker.deprecated_apis[0].api_name, "OldContext");
assert_eq!(checker.deprecated_apis.len(), 3); }
#[tokio::test]
async fn test_check_compatibility() {
let checker: _ = V8CompatibilityChecker::new();
let report: _ = checker.check_compatibility().await.unwrap();
assert!(!report.v8_version.is_empty());
assert!(!report.rusty_v8_version.is_empty());
assert_eq!(report.summary.total_apis, checker.api_map.len());
assert!(report.summary.compatibility_percentage >= 0.0);
assert!(report.summary.compatibility_percentage <= 100.0);
}
#[test]
fn test_compatibility_score_calculation() {
let checker: _ = V8CompatibilityChecker::new();
let report: _ = CompatibilityReport {
v8_version: "12.3.0".to_string(),
rusty_v8_version: "0.22.0".to_string(),
summary: CompatibilitySummary {
total_apis: 100,
compatible_apis: 80,
deprecated_apis: 15,
experimental_apis: 5,
compatibility_percentage: 80.0,
},
api_map: HashMap::new(),
deprecated_apis: Vec::new(),
migration_plans: Vec::new(),
};
let score: _ = checker.calculate_compatibility_score(&report);
assert!(score >= 0.0);
assert!(score <= 100.0);
assert!(score > 70.0); }
#[tokio::test]
async fn test_generate_migration_guide() {
let checker: _ = V8CompatibilityChecker::new();
let guide: _ = checker.generate_migration_guide().await.unwrap();
assert!(guide.total_deprecated > 0);
assert!(guide.migration_steps.len() > 0);
assert!(!guide.recommendations.is_empty());
}
#[tokio::test]
async fn test_auto_fix_compatibility() {
let checker: _ = V8CompatibilityChecker::new();
let fixes: _ = checker.auto_fix_compatibility().await.unwrap();
assert!(!fixes.is_empty());
assert_eq!(fixes[0].api_name, "HandleScope::Empty");
assert_eq!(fixes[0].status, "fixed");
assert!(fixes[0].verified);
}
#[tokio::test]
async fn test_verify_fixes() {
let checker: _ = V8CompatibilityChecker::new();
let fixes: _ = vec![
AutoFixResult {
api_name: "API1".to_string(),
status: "fixed".to_string(),
changes: vec!["change1".to_string()],
verified: true,
},
AutoFixResult {
api_name: "API2".to_string(),
status: "fixed".to_string(),
changes: vec!["change2".to_string()],
verified: true,
},
];
let report: _ = checker.verify_fixes(&fixes).await.unwrap();
assert_eq!(report.total_fixes, 2);
assert_eq!(report.passed, 2);
assert_eq!(report.failed, 0);
assert_eq!(report.success_rate, 100.0);
}
#[tokio::test]
async fn test_get_v8_info() {
let checker: _ = V8CompatibilityChecker::new();
let info: _ = checker.get_v8_info().await.unwrap();
assert!(!info.v8_version.is_empty());
assert!(!info.rusty_v8_version.is_empty());
assert!(!info.features.is_empty());
}
#[test]
fn test_estimate_migration_effort() {
let checker: _ = V8CompatibilityChecker::new();
assert_eq!(checker.estimate_migration_effort("OldContext"), "2-3 小时");
assert_eq!(checker.estimate_migration_effort("HandleScope::Empty"), "1-2 小时");
assert_eq!(checker.estimate_migration_effort("V8::Initialize"), "30 分钟");
assert_eq!(checker.estimate_migration_effort("UnknownAPI"), "1-4 小时");
}
#[tokio::test]
async fn test_check_rusty_v8_version() {
let checker: _ = V8CompatibilityChecker::new();
let is_compatible: _ = checker.check_rusty_v8_version().await.unwrap();
assert!(is_compatible); }
}