"""Validation utilities for Unreal MCP Server.""" import re from pathlib import Path def validate_path(path: str | Path, must_exist: bool = False) -> Path: """Validate and normalize a file path. Args: path: Path to validate must_exist: If True, check that the path exists Returns: Normalized Path object Raises: ValueError: If path is invalid or doesn't exist (when must_exist=True) """ path = Path(path) if must_exist and not path.exists(): raise ValueError(f"Path does not exist: {path}") return path def validate_class_name(name: str) -> str: """Validate an Unreal class name. Args: name: Class name to validate (e.g., 'BP_Enemy', 'AWeapon') Returns: Validated class name Raises: ValueError: If class name is invalid """ # Unreal class names should be alphanumeric with underscores # Typically prefixed with A (Actor), U (Object), F (Struct), E (Enum) # Or BP_ for Blueprints pattern = r"^[A-Z][A-Za-z0-9_]*$" if not re.match(pattern, name): raise ValueError( f"Invalid class name: {name}. " "Must start with uppercase letter and contain only alphanumeric characters and underscores." ) return name def validate_unreal_path(path: str) -> str: """Validate an Unreal content path. Args: path: Unreal path (e.g., '/Game/Weapons/BP_Rifle') Returns: Validated path Raises: ValueError: If path is invalid """ if not path.startswith("/"): raise ValueError(f"Unreal path must start with '/': {path}") # Common valid prefixes valid_prefixes = ["/Game/", "/Engine/", "/Script/"] if not any(path.startswith(prefix) for prefix in valid_prefixes): raise ValueError( f"Unreal path must start with one of {valid_prefixes}: {path}" ) return path def validate_location(location: list[float]) -> tuple[float, float, float]: """Validate a 3D location. Args: location: List of [X, Y, Z] coordinates Returns: Tuple of (X, Y, Z) Raises: ValueError: If location is invalid """ if len(location) != 3: raise ValueError(f"Location must have 3 components [X, Y, Z], got {len(location)}") return tuple(float(v) for v in location) # type: ignore def validate_rotation(rotation: list[float]) -> tuple[float, float, float]: """Validate a 3D rotation. Args: rotation: List of [Pitch, Yaw, Roll] angles in degrees Returns: Tuple of (Pitch, Yaw, Roll) Raises: ValueError: If rotation is invalid """ if len(rotation) != 3: raise ValueError(f"Rotation must have 3 components [Pitch, Yaw, Roll], got {len(rotation)}") return tuple(float(v) for v in rotation) # type: ignore