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

1"""Form validation service for Textual TUI.""" 

2 

3from typing import Any, Dict, List, Optional, Union 

4from pathlib import Path 

5from enum import Enum 

6 

7 

8class ValidationResult: 

9 """Result of field validation.""" 

10 

11 def __init__(self, is_valid: bool, error_message: Optional[str] = None): 

12 self.is_valid = is_valid 

13 self.error_message = error_message 

14 

15 

16class ValidationService: 

17 """Service for validating form field values.""" 

18 

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") 

28 

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) 

45 

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") 

52 

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") 

61 

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") 

70 

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") 

77 

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") 

86 

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") 

96 

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 = {} 

101 

102 for spec in field_specs: 

103 field_name = spec.name 

104 field_value = field_values.get(field_name) 

105 

106 result = ValidationService.validate_field( 

107 spec.actual_type, 

108 field_value, 

109 spec.is_optional 

110 ) 

111 

112 if not result.is_valid: 

113 errors[field_name] = result.error_message 

114 

115 return errors