use super::parser::{SolidityAST, Contract, Function, Visibility};
use super::types::TypeMapper;
#[derive(Debug, Clone)]
pub struct AnalysisReport {
pub compatibility_score: f64,
pub warnings: Vec<String>,
pub errors: Vec<String>,
pub suggestions: Vec<String>,
pub unsupported_features: Vec<String>,
pub used_libraries: Vec<String>,
}
pub struct ConversionAnalyzer {
type_mapper: TypeMapper,
}
impl ConversionAnalyzer {
pub fn new() -> Self {
Self {
type_mapper: TypeMapper::new(),
}
}
pub fn analyze(&self, ast: SolidityAST) -> Result<AnalysisReport, String> {
let mut warnings = Vec::new();
let mut errors = Vec::new();
let mut suggestions = Vec::new();
let mut unsupported_features = Vec::new();
let has_openzeppelin_import = ast.imports.iter().any(|s| {
let lower = s.to_lowercase();
lower.contains("openzeppelin") || lower.contains("@openzeppelin")
});
if has_openzeppelin_import {
suggestions.push("File imports OpenZeppelin - DAL has built-in reentrancy protection; modifiers can be mapped to @secure".to_string());
}
for contract in &ast.contracts {
self.analyze_contract(contract, &mut warnings, &mut errors, &mut suggestions, &mut unsupported_features);
}
let compatibility_score = self.calculate_compatibility_score(&warnings, &errors, &unsupported_features);
let used_libraries = ast.imports.clone();
Ok(AnalysisReport {
compatibility_score,
warnings,
errors,
suggestions,
unsupported_features,
used_libraries,
})
}
fn analyze_contract(&self, contract: &Contract, warnings: &mut Vec<String>,
errors: &mut Vec<String>, suggestions: &mut Vec<String>,
unsupported_features: &mut Vec<String>) {
match contract.kind {
super::parser::ContractKind::Interface => {
warnings.push(format!("Interface '{}' - will be converted to service", contract.name));
},
super::parser::ContractKind::Abstract => {
warnings.push(format!("Abstract contract '{}' - abstract features may need manual conversion", contract.name));
},
super::parser::ContractKind::Library => {
unsupported_features.push(format!("Library '{}' - libraries not directly supported", contract.name));
},
_ => {},
}
if !contract.inheritance.is_empty() {
warnings.push(format!("Contract '{}' uses inheritance - may need manual review", contract.name));
}
for func in &contract.functions {
self.analyze_function(func, warnings, errors, suggestions);
}
if super::security::SecurityConverter::new().has_reentrancy_risk(contract) {
suggestions.push(format!("Contract '{}' should use @secure attribute for reentrancy protection", contract.name));
}
if self.uses_openzeppelin(contract) {
suggestions.push(format!("Contract '{}' uses OpenZeppelin - consider using DAL built-in security features", contract.name));
}
}
fn analyze_function(&self, func: &Function, warnings: &mut Vec<String>,
_errors: &mut Vec<String>, suggestions: &mut Vec<String>) {
if matches!(func.visibility, Visibility::External) {
warnings.push(format!("Function '{}' is external - will be converted to @public", func.name));
}
if matches!(func.mutability, super::parser::Mutability::Payable) {
suggestions.push(format!("Function '{}' is payable - ensure proper handling in DAL", func.name));
}
if !func.modifiers.is_empty() {
warnings.push(format!("Function '{}' uses modifiers - may need manual conversion", func.name));
}
for ret in &func.returns {
if !self.type_mapper.is_supported(&ret.param_type) {
warnings.push(format!("Function '{}' returns unsupported type '{}'", func.name, ret.param_type));
}
}
for param in &func.parameters {
if !self.type_mapper.is_supported(¶m.param_type) {
warnings.push(format!("Function '{}' parameter '{}' has unsupported type '{}'",
func.name, param.name, param.param_type));
}
}
}
fn uses_openzeppelin(&self, contract: &Contract) -> bool {
contract.functions.iter().any(|f| {
f.modifiers.iter().any(|m|
m.contains("onlyOwner") ||
m.contains("nonReentrant") ||
m.contains("whenNotPaused")
)
})
}
fn calculate_compatibility_score(&self, warnings: &[String], errors: &[String],
unsupported: &[String]) -> f64 {
let mut score = 100.0;
score -= (errors.len() as f64) * 20.0;
score -= (unsupported.len() as f64) * 15.0;
score -= (warnings.len() as f64) * 5.0;
score.max(0.0).min(100.0)
}
}
impl Default for ConversionAnalyzer {
fn default() -> Self {
Self::new()
}
}