Coverage for openhcs/textual_tui/services/validation_service.py: 0.0%
76 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-14 05:57 +0000
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-14 05:57 +0000
1"""Form validation service for Textual TUI."""
3from typing import Any, Dict, List, Optional, Union
4from pathlib import Path
5from enum import Enum
8class ValidationResult:
9 """Result of field validation."""
11 def __init__(self, is_valid: bool, error_message: Optional[str] = None):
12 self.is_valid = is_valid
13 self.error_message = error_message
16class ValidationService:
17 """Service for validating form field values."""
19 @staticmethod
20 def validate_field(field_type: type, value: Any, is_optional: bool = False) -> ValidationResult:
21 """Validate a field value against its expected type."""
22 # Handle optional fields
23 if value is None or value == "":
24 if is_optional:
25 return ValidationResult(True)
26 else:
27 return ValidationResult(False, "Field is required")
29 # Type-specific validation
30 if field_type == bool:
31 return ValidationService._validate_bool(value)
32 elif field_type == int:
33 return ValidationService._validate_int(value)
34 elif field_type == float:
35 return ValidationService._validate_float(value)
36 elif field_type == str:
37 return ValidationService._validate_str(value)
38 elif field_type == Path:
39 return ValidationService._validate_path(value)
40 elif isinstance(field_type, type) and issubclass(field_type, Enum):
41 return ValidationService._validate_enum(value, field_type)
42 else:
43 # Unknown type - accept as string
44 return ValidationResult(True)
46 @staticmethod
47 def _validate_bool(value: Any) -> ValidationResult:
48 """Validate boolean value."""
49 if isinstance(value, bool):
50 return ValidationResult(True)
51 return ValidationResult(False, "Must be true or false")
53 @staticmethod
54 def _validate_int(value: Any) -> ValidationResult:
55 """Validate integer value."""
56 try:
57 int(value)
58 return ValidationResult(True)
59 except (ValueError, TypeError):
60 return ValidationResult(False, "Must be a valid integer")
62 @staticmethod
63 def _validate_float(value: Any) -> ValidationResult:
64 """Validate float value."""
65 try:
66 float(value)
67 return ValidationResult(True)
68 except (ValueError, TypeError):
69 return ValidationResult(False, "Must be a valid number")
71 @staticmethod
72 def _validate_str(value: Any) -> ValidationResult:
73 """Validate string value."""
74 if isinstance(value, str):
75 return ValidationResult(True)
76 return ValidationResult(False, "Must be text")
78 @staticmethod
79 def _validate_path(value: Any) -> ValidationResult:
80 """Validate path value."""
81 try:
82 Path(str(value))
83 return ValidationResult(True)
84 except Exception:
85 return ValidationResult(False, "Must be a valid path")
87 @staticmethod
88 def _validate_enum(value: Any, enum_type: type) -> ValidationResult:
89 """Validate enum value."""
90 try:
91 if value in [member.value for member in enum_type]:
92 return ValidationResult(True)
93 return ValidationResult(False, f"Must be one of: {[m.value for m in enum_type]}")
94 except Exception:
95 return ValidationResult(False, "Invalid enum value")
97 @staticmethod
98 def validate_form(field_specs: List[Any], field_values: Dict[str, Any]) -> Dict[str, str]:
99 """Validate entire form and return error messages."""
100 errors = {}
102 for spec in field_specs:
103 field_name = spec.name
104 field_value = field_values.get(field_name)
106 result = ValidationService.validate_field(
107 spec.actual_type,
108 field_value,
109 spec.is_optional
110 )
112 if not result.is_valid:
113 errors[field_name] = result.error_message
115 return errors