{"""Rust endpoint detector for Actix-web.""" import re from pathlib import Path from docgen.detectors.base import BaseDetector from docgen.models import Endpoint, HTTPMethod class RustDetector(BaseDetector): """Detector for Rust web frameworks.""" extensions = [".rs"] framework_name = "rust" ACTIX_PATTERN = re.compile( r'(?:route|service)\.("|\')(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)("|\')\s*\.?\s*(to|handler)', re.MULTILINE, ) ACTIX_WEB_PATTERN = re.compile( r'(?:App::new\(\)|scope|service)\.route\s*\(\s*"([^"]+)"\s*,\s*([a-zA-Z_][a-zA-Z0-9_]*)', re.MULTILINE, ) ACTIX_MACRO_PATTERN = re.compile( r'#\[route\s*\(\s*"([^"]+)"\s*,\s*method\s*=\s*(?:HttpMethod::)?(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)', re.MULTILINE, ) HTTP_METHODS = {"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"} METHOD_MAP = { "GET": HTTPMethod.GET, "POST": HTTPMethod.POST, "PUT": HTTPMethod.PUT, "PATCH": HTTPMethod.PATCH, "DELETE": HTTPMethod.DELETE, "OPTIONS": HTTPMethod.OPTIONS, "HEAD": HTTPMethod.HEAD, } def detect_endpoints(self, file_path: Path) -> list[Endpoint]: """Detect endpoints in a Rust file.""" content = file_path.read_text() endpoints = [] if self._detect_actix(content): endpoints.extend(self._detect_actix_endpoints(content, file_path)) return endpoints def _detect_actix(self, content: str) -> bool: """Check if file uses Actix-web.""" indicators = [ 'actix_web', 'actix-web', 'actix::', 'HttpResponse', 'web::Resource', ] return any(indicator in content for indicator in indicators) def _detect_actix_endpoints(self, content: str, file_path: Path) -> list[Endpoint]: """Detect Actix-web endpoints.""" endpoints = [] for match in self.ACTIX_WEB_PATTERN.finditer(content): path, handler = match.groups() endpoint = Endpoint( path=path, method=HTTPMethod.GET, summary=f"GET {path}", description=f"Handler: {handler}", file_path=str(file_path), line_number=content[:match.start()].count("\n") + 1, ) endpoints.append(endpoint) for match in self.ACTIX_MACRO_PATTERN.finditer(content): path, method = match.groups() endpoint = Endpoint( path=path, method=self.METHOD_MAP.get(method, HTTPMethod.GET), summary=f"{method} {path}", file_path=str(file_path), line_number=content[:match.start()].count("\n") + 1, ) endpoints.append(endpoint) return endpoints