use super::parser::{Contract, Function, SolidityAST, 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()
}
}