from __future__ import annotations import tomllib from pathlib import Path from typing import Any from depaudit.parsers import Parser, ParsedManifest, Dependency class RustParser(Parser): language = "rust" def can_parse(self, file_path: Path) -> bool: return file_path.name in ("Cargo.toml", "Cargo.lock") def parse(self, file_path: Path) -> ParsedManifest: with open(file_path, "rb") as f: data = tomllib.load(f) manifest = ParsedManifest( language=self.language, file_path=file_path, ) if file_path.name == "Cargo.toml": self._parse_cargo_toml(file_path, data, manifest) elif file_path.name == "Cargo.lock": self._parse_cargo_lock(file_path, data, manifest) return manifest def _parse_cargo_toml( self, file_path: Path, data: dict[str, Any], manifest: ParsedManifest ) -> None: manifest.project_name = data.get("package", {}).get("name") manifest.project_version = data.get("package", {}).get("version") dependencies = data.get("dependencies", {}) for name, dep_info in dependencies.items(): if isinstance(dep_info, str): version = dep_info manifest.dependencies.append( self._create_dependency(file_path, name, version) ) elif isinstance(dep_info, dict): version = dep_info.get("version", "") optional = dep_info.get("optional", False) manifest.dependencies.append( self._create_dependency( file_path, name, version, optional=optional ) ) dev_dependencies = data.get("dev-dependencies", {}) for name, dep_info in dev_dependencies.items(): if isinstance(dep_info, str): version = dep_info manifest.dependencies.append( self._create_dependency(file_path, name, version, dev=True) ) elif isinstance(dep_info, dict): version = dep_info.get("version", "") manifest.dependencies.append( self._create_dependency(file_path, name, version, dev=True) ) build_dependencies = data.get("build-dependencies", {}) for name, dep_info in build_dependencies.items(): if isinstance(dep_info, str): version = dep_info manifest.dependencies.append( self._create_dependency(file_path, name, version, dev=True) ) elif isinstance(dep_info, dict): version = dep_info.get("version", "") manifest.dependencies.append( self._create_dependency(file_path, name, version, dev=True) ) manifest.raw_data = data def _parse_cargo_lock( self, file_path: Path, data: dict[str, Any], manifest: ParsedManifest ) -> None: package = data.get("package", []) for pkg in package: name = pkg.get("name", "") version = pkg.get("version", "") source = pkg.get("source", "") if source and ("registry" in source or "git" in source or "local" in source): manifest.dependencies.append( self._create_dependency(file_path, name, version) )