"""Common validation utility functions.""" import re from pathlib import Path from typing import Any, Optional # Password validation constants MIN_PASSWORD_LENGTH = 8 def validate_email(email: str) -> bool: """Validate email address format. Args: email: Email address to validate Returns: True if email format is valid, False otherwise """ pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$" return bool(re.match(pattern, email)) def validate_password_strength(password: str) -> tuple[bool, str | None]: """Validate password meets security requirements. Args: password: Password to validate Returns: Tuple of (is_valid, error_message) """ if len(password) < MIN_PASSWORD_LENGTH: msg = f"Password must be at least {MIN_PASSWORD_LENGTH} characters long" return False, msg if not re.search(r"[A-Z]", password): return False, "Password must contain at least one uppercase letter" if not re.search(r"[a-z]", password): return False, "Password must contain at least one lowercase letter" if not re.search(r"\d", password): return False, "Password must contain at least one number" return True, None def validate_filename( filename: str, allowed_extensions: list[str] | None = None ) -> bool: """Validate filename format and extension. Args: filename: Filename to validate allowed_extensions: List of allowed file extensions (with dots) Returns: True if filename is valid, False otherwise """ if not filename or filename.startswith(".") or "/" in filename or "\\" in filename: return False if allowed_extensions: file_path = Path(filename) return file_path.suffix.lower() in [ext.lower() for ext in allowed_extensions] return True def validate_audio_filename(filename: str) -> bool: """Validate audio filename has allowed extension. Args: filename: Audio filename to validate Returns: True if filename has valid audio extension, False otherwise """ audio_extensions = [".mp3", ".wav", ".flac", ".ogg", ".m4a", ".aac", ".wma"] return validate_filename(filename, audio_extensions) def sanitize_filename(filename: str) -> str: """Sanitize filename by removing/replacing invalid characters. Args: filename: Filename to sanitize Returns: Sanitized filename safe for filesystem """ # Remove or replace problematic characters sanitized = re.sub(r'[<>:"/\\|?*]', "_", filename) # Remove leading/trailing whitespace and dots sanitized = sanitized.strip(" .") # Ensure not empty if not sanitized: sanitized = "untitled" return sanitized def validate_url(url: str) -> bool: """Validate URL format. Args: url: URL to validate Returns: True if URL format is valid, False otherwise """ pattern = r"^https?://[^\s/$.?#].[^\s]*$" return bool(re.match(pattern, url)) def validate_positive_integer(value: Any, field_name: str = "value") -> int: """Validate and convert value to positive integer. Args: value: Value to validate and convert field_name: Name of field for error messages Returns: Validated positive integer Raises: ValueError: If value is not a positive integer """ try: int_value = int(value) if int_value <= 0: msg = f"{field_name} must be a positive integer" raise ValueError(msg) return int_value except (TypeError, ValueError) as e: msg = f"{field_name} must be a positive integer" raise ValueError(msg) from e