diff --git a/codesnap/codesnap/output/json_exporter.py b/codesnap/codesnap/output/json_exporter.py new file mode 100644 index 0000000..316dc30 --- /dev/null +++ b/codesnap/codesnap/output/json_exporter.py @@ -0,0 +1,118 @@ +"""JSON export module for CodeSnap.""" + +import json +from dataclasses import asdict, dataclass +from datetime import datetime +from pathlib import Path +from typing import Any, Optional + +from ..core.extractor import ExtractedCode + + +@dataclass +class FileSummary: + """Summary information for a file.""" + + path: str + language: str + function_count: int + class_count: int + complexity: str + line_count: int + + +@dataclass +class CodeSummary: + """Complete code summary for export.""" + + timestamp: str + version: str + directory: str + file_count: int + language_breakdown: dict[str, int] + files: list[dict[str, Any]] + dependencies: dict[str, list[str]] + metrics: dict[str, Any] + + +class JSONExporter: + """Exports code summaries in JSON format.""" + + def __init__(self, version: str = "0.1.0") -> None: + self.version = version + + def export( + self, + extracted_files: list[ExtractedCode], + file_paths: list[Path], + dependency_data: dict[str, Any], + complexity_data: dict[str, str], + output_path: Optional[Path] = None, + ) -> str: + """Export code summary to JSON.""" + language_counts: dict[str, int] = {} + file_summaries: list[dict[str, Any]] = [] + + for extracted in extracted_files: + lang = extracted.language + language_counts[lang] = language_counts.get(lang, 0) + 1 + + file_summary = { + "path": str(extracted.file_path), + "language": lang, + "functions": [ + { + "name": f.name, + "start_line": f.start_line, + "end_line": f.end_line, + "parameters": f.parameters, + "return_type": f.return_type, + "is_method": f.is_method, + "class_name": f.class_name, + } + for f in extracted.functions + ], + "classes": [ + { + "name": c.name, + "start_line": c.start_line, + "end_line": c.end_line, + "methods": [m.name for m in c.methods], + "base_classes": c.base_classes, + } + for c in extracted.classes + ], + "complexity": complexity_data.get(str(extracted.file_path), "unknown"), + } + file_summaries.append(file_summary) + + dependencies: dict[str, list[str]] = {} + for dep in dependency_data.get("dependencies", []): + source = str(dep.source) + target = str(dep.target) + if source not in dependencies: + dependencies[source] = [] + dependencies[source].append(target) + + summary = CodeSummary( + timestamp=datetime.utcnow().isoformat() + "Z", + version=self.version, + directory=str(file_paths[0].parent) if file_paths else "", + file_count=len(file_summaries), + language_breakdown=language_counts, + files=file_summaries, + dependencies=dependencies, + metrics={ + "total_functions": sum(len(f.functions) for f in extracted_files), + "total_classes": sum(len(f.classes) for f in extracted_files), + "cycles_found": len(dependency_data.get("cycles", [])), + "orphaned_files": len(dependency_data.get("orphaned", [])), + }, + ) + + result = asdict(summary) + + if output_path: + output_path.write_text(json.dumps(result, indent=2), encoding="utf-8") + + return json.dumps(result, indent=2)