"""
Common utilities for error handling and validation across the Wordle Solver.
"""
from functools import wraps
from typing import Callable, TypeVar
from src.modules.backend.exceptions import (
InvalidGuessError,
InvalidResultError,
WordleError,
)
from src.modules.backend.result_color import ResultColor
T = TypeVar("T")
[docs]
def validate_result_pattern(result: str, expected_length: int = 5) -> str:
"""
Validate result pattern ensuring it contains only valid color codes.
Args:
result: The result pattern to validate
expected_length: Expected pattern length (default: 5)
Returns:
The validated and normalized result pattern (uppercase)
Raises:
InvalidResultError: If the result pattern is invalid
"""
if not result:
raise InvalidResultError(result, "cannot be empty")
# Normalize to uppercase
result = result.upper().strip()
if len(result) != expected_length:
raise InvalidResultError(
result, f"must be exactly {expected_length} characters"
)
if not ResultColor.is_valid_result_string(result):
valid_chars = [color.value for color in ResultColor]
raise InvalidResultError(result, f"must contain only {', '.join(valid_chars)}")
return result
[docs]
def safe_execute(operation_name: str = "operation"):
"""
Decorator to safely execute operations and handle common exceptions.
Args:
operation_name: Name of the operation for error messages
"""
def decorator(func: Callable[..., T]) -> Callable[..., T]:
@wraps(func)
def wrapper(*args, **kwargs) -> T:
try:
return func(*args, **kwargs)
except WordleError:
# Re-raise Wordle-specific errors as they're already properly formatted
raise
except Exception as e:
# Wrap unexpected errors with proper exception chaining
raise WordleError(
f"Unexpected error in {operation_name}: {str(e)}"
) from e
return wrapper
return decorator
[docs]
def format_guess_history(guesses_history: list) -> str:
"""
Format guess history for display purposes.
Args:
guesses_history: List of [guess, result, strategy] tuples
Returns:
Formatted string representation of the guess history
"""
if not guesses_history:
return "No guesses made."
lines = ["Guess History:"]
for i, guess_data in enumerate(guesses_history, 1):
if len(guess_data) >= 3:
guess, result, strategy = guess_data[:3]
lines.append(f"{i:2d}. {guess} -> {result} (using {strategy})")
else:
guess, result = guess_data[:2]
lines.append(f"{i:2d}. {guess} -> {result}")
return "\n".join(lines)
[docs]
def calculate_success_metrics(guesses_history: list, won: bool) -> dict:
"""
Calculate various success metrics from guess history.
Args:
guesses_history: List of guess attempts
won: Whether the game was won
Returns:
Dictionary containing success metrics
"""
total_attempts = len(guesses_history)
metrics = {
"total_attempts": total_attempts,
"won": won,
"success_rate": 1.0 if won else 0.0,
"efficiency_score": 0.0,
}
if won and total_attempts > 0:
# Calculate efficiency (6 attempts is max, 1 is perfect)
metrics["efficiency_score"] = (7 - total_attempts) / 6.0
return metrics