morph-cli 0.1.0

AST-based codebase migration and codemod tool for JavaScript and TypeScript projects.
Documentation
#![allow(clippy::all, unused)]
use std::collections::HashMap;

#[derive(Debug, Clone, Default)]
#[allow(unused)]
pub struct MigrationAnalysis {
    pub routes: Vec<RouteInfo>,
    pub express_apps: Vec<usize>,
    pub middleware_count: usize,
    pub has_body_parser: bool,
    pub has_auth_middleware: bool,
    pub has_error_handler: bool,
    pub async_handlers: usize,
    pub complexity: ComplexityLevel,
    pub risky_patterns: Vec<RiskyPattern>,
    pub suggested_recipes: Vec<String>,
    pub migration_blockers: Vec<String>,
    pub unsupported_apis: Vec<String>,
    pub estimated_difficulty: u8,
}

#[derive(Debug, Clone, PartialEq)]
pub enum ComplexityLevel {
    Simple,
    Moderate,
    Complex,
    Unsupported,
}

impl Default for ComplexityLevel {
    fn default() -> Self {
        Self::Simple
    }
}

#[derive(Debug, Clone)]
#[allow(unused)]
pub struct RouteInfo {
    pub method: String,
    pub path: String,
    pub handler_type: String,
    pub middleware_count: usize,
    pub estimated_complexity: u8,
}

#[derive(Debug, Clone)]
#[allow(unused)]
pub struct RiskyPattern {
    pub pattern: String,
    pub location: String,
    pub severity: u8,
    pub description: String,
}

impl MigrationAnalysis {
    pub fn classify_complexity(&mut self) {
        let route_count = self.routes.len();
        let middleware = self.middleware_count;
        let async_count = self.async_handlers;
        let risky = self.risky_patterns.len();

        if risky > 3 || self.migration_blockers.len() > 2 {
            self.complexity = ComplexityLevel::Unsupported;
        } else if risky > 1 || middleware > 5 || route_count > 20 {
            self.complexity = ComplexityLevel::Complex;
        } else if middleware > 2 || route_count > 5 || async_count > 2 {
            self.complexity = ComplexityLevel::Moderate;
        } else {
            self.complexity = ComplexityLevel::Simple;
        }
    }

    pub fn detect_risky_patterns(&mut self) {
        if self.has_error_handler {
            self.risky_patterns.push(RiskyPattern {
                pattern: "custom_error_handler".to_string(),
                location: "middleware".to_string(),
                severity: 60,
                description: "Custom error middleware may need manual migration".to_string(),
            });
        }

        if self.has_auth_middleware {
            self.risky_patterns.push(RiskyPattern {
                pattern: "auth_middleware".to_string(),
                location: "middleware".to_string(),
                severity: 50,
                description: "Auth middleware should be reviewed for Fastify plugin compatibility"
                    .to_string(),
            });
        }

        if self.async_handlers > self.routes.len() / 2 {
            self.risky_patterns.push(RiskyPattern {
                pattern: "high_async_ratio".to_string(),
                location: "routes".to_string(),
                severity: 40,
                description: "High async handler ratio - ensure proper error handling".to_string(),
            });
        }
    }

    pub fn suggest_recipes(&mut self) {
        match self.complexity {
            ComplexityLevel::Simple => {
                self.suggested_recipes
                    .push("express-to-fastify:basic".to_string());
            }
            ComplexityLevel::Moderate => {
                self.suggested_recipes
                    .push("express-to-fastify:basic".to_string());
                self.suggested_recipes
                    .push("express-to-fastify:middleware".to_string());
            }
            ComplexityLevel::Complex | ComplexityLevel::Unsupported => {
                self.suggested_recipes
                    .push("express-to-fastify:review-required".to_string());
                self.migration_blockers
                    .push("Manual review required for complex middleware".to_string());
            }
        }

        if self.has_body_parser {
            self.suggested_recipes
                .push("fastify:@fastify/json".to_string());
        }
    }

    pub fn unsupported_api(&mut self, api: &str) {
        if !self.unsupported_apis.contains(&api.to_string()) {
            self.unsupported_apis.push(api.to_string());
        }
    }

    pub fn add_blocker(&mut self, blocker: &str) {
        if !self.migration_blockers.contains(&blocker.to_string()) {
            self.migration_blockers.push(blocker.to_string());
        }
    }

    pub fn statistics(&self) -> AnalysisStats {
        AnalysisStats {
            total_routes: self.routes.len(),
            async_routes: self.async_handlers,
            sync_routes: self.routes.len() - self.async_handlers,
            middleware: self.middleware_count,
            estimated_lines: self.routes.len() * 10,
            complexity_score: self.estimated_difficulty,
        }
    }
}

#[derive(Debug)]
pub struct AnalysisStats {
    pub total_routes: usize,
    pub async_routes: usize,
    pub sync_routes: usize,
    pub middleware: usize,
    pub estimated_lines: usize,
    pub complexity_score: u8,
}

impl AnalysisStats {
    pub fn print_summary(&self) {
        println!();
        println!(
            "  Routes: {} (async: {}, sync: {})",
            self.total_routes, self.async_routes, self.sync_routes
        );
        println!("  Middleware: {}", self.middleware);
        println!("  Estimated complexity: {}/100", self.complexity_score);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_simple_classification() {
        let mut analysis = MigrationAnalysis::default();
        analysis.routes.push(RouteInfo {
            method: "get".to_string(),
            path: "/".to_string(),
            handler_type: "sync".to_string(),
            middleware_count: 0,
            estimated_complexity: 1,
        });
        analysis.classify_complexity();
        assert_eq!(analysis.complexity, ComplexityLevel::Simple);
    }

    #[test]
    fn test_complex_classification() {
        let mut analysis = MigrationAnalysis::default();
        analysis.middleware_count = 10;
        analysis.classify_complexity();
        assert_eq!(analysis.complexity, ComplexityLevel::Complex);
    }

    #[test]
    fn test_statistics() {
        let mut analysis = MigrationAnalysis::default();
        analysis.routes.push(RouteInfo {
            method: "get".to_string(),
            path: "/".to_string(),
            handler_type: "async".to_string(),
            middleware_count: 2,
            estimated_complexity: 1,
        });
        analysis.async_handlers = 1;
        analysis.middleware_count = 2;
        let stats = analysis.statistics();
        assert_eq!(stats.total_routes, 1);
        assert_eq!(stats.async_routes, 1);
    }
}