""" Data models for solution and project management. """ from dataclasses import dataclass, field from pathlib import Path from typing import Optional @dataclass class CsprojProject: """Represents a .csproj project file.""" path: Path # Absolute path to .csproj file name: str # Project name (without extension) guid: str # Project GUID (generated deterministically from path) project_references: list[Path] = field(default_factory=list) # Resolved absolute paths package_references: dict[str, str] = field(default_factory=dict) # Package name -> version def __hash__(self) -> int: return hash(self.path) def __eq__(self, other: object) -> bool: if not isinstance(other, CsprojProject): return False return self.path == other.path @dataclass class SolutionFolder: """Represents a solution folder in a .sln file.""" name: str # Folder display name guid: str # Folder GUID path: str # Full path within solution (e.g., "Module/__Libraries") parent_guid: Optional[str] = None # Parent folder GUID (None for root folders) children: list["SolutionFolder"] = field(default_factory=list) projects: list[CsprojProject] = field(default_factory=list) def __hash__(self) -> int: return hash(self.path) def __eq__(self, other: object) -> bool: if not isinstance(other, SolutionFolder): return False return self.path == other.path @dataclass class PackageUsage: """Tracks usage of a NuGet package across the codebase.""" package_name: str usages: dict[Path, str] = field(default_factory=dict) # csproj path -> version string def get_all_versions(self) -> list[str]: """Get list of unique versions used.""" return list(set(self.usages.values())) def get_usage_count(self) -> int: """Get number of projects using this package.""" return len(self.usages) @dataclass class NormalizationChange: """Represents a version change for a package in a project.""" csproj_path: Path old_version: str new_version: str @dataclass class NormalizationResult: """Result of normalizing a package across the codebase.""" package_name: str target_version: str changes: list[NormalizationChange] = field(default_factory=list) skipped_reason: Optional[str] = None # Constants for solution file format CSHARP_PROJECT_TYPE_GUID = "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC" SOLUTION_FOLDER_TYPE_GUID = "2150E333-8FDC-42A3-9474-1A3956D46DE8" BYPASS_MARKER = "# STELLAOPS-MANUAL-SOLUTION"