Coverage for src/prosemark/freewriting/ports/cli_adapter.py: 100%

45 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-09-24 18:08 +0000

1"""CLI adapter port interfaces for freewriting command interface. 

2 

3This module defines the port interfaces for the command-line interface 

4components of the freewriting feature. 

5""" 

6 

7from __future__ import annotations 

8 

9from abc import ABC, abstractmethod 

10from dataclasses import dataclass 

11from typing import TYPE_CHECKING 

12 

13if TYPE_CHECKING: # pragma: no cover 

14 from prosemark.freewriting.domain.models import SessionConfig 

15 from prosemark.freewriting.ports.tui_adapter import TUIAdapterPort, TUIConfig 

16 

17 

18class CLIAdapterPort(ABC): 

19 """Port interface for CLI operations. 

20 

21 This port defines the contract for command-line interface operations 

22 such as argument parsing and TUI launching. 

23 """ 

24 

25 @property 

26 @abstractmethod 

27 def tui_adapter(self) -> TUIAdapterPort: 

28 """TUI adapter instance for launching interface. 

29 

30 Returns: 

31 The TUI adapter instance used by this CLI adapter. 

32 

33 """ 

34 

35 @abstractmethod 

36 def parse_arguments( 

37 self, 

38 node: str | None, 

39 title: str | None, 

40 word_count_goal: int | None, 

41 time_limit: int | None, 

42 theme: str, 

43 current_directory: str | None, 

44 ) -> SessionConfig: 

45 """Parse and validate CLI arguments into session configuration. 

46 

47 Args: 

48 node: Optional UUID of target node. 

49 title: Optional session title. 

50 word_count_goal: Optional word count target. 

51 time_limit: Optional time limit in seconds. 

52 theme: UI theme name. 

53 current_directory: Working directory override. 

54 

55 Returns: 

56 Validated SessionConfig object. 

57 

58 Raises: 

59 ValidationError: If any arguments are invalid. 

60 

61 """ 

62 

63 @abstractmethod 

64 def validate_node_argument(self, node: str | None) -> str | None: 

65 """Validate node UUID argument. 

66 

67 Args: 

68 node: Node UUID string to validate. 

69 

70 Returns: 

71 Validated UUID string or None. 

72 

73 Raises: 

74 ValidationError: If UUID format is invalid. 

75 

76 """ 

77 

78 @abstractmethod 

79 def create_tui_config(self, theme: str) -> TUIConfig: 

80 """Create TUI configuration from CLI arguments. 

81 

82 Args: 

83 theme: Theme name from CLI. 

84 

85 Returns: 

86 TUIConfig object with appropriate settings. 

87 

88 Raises: 

89 ValidationError: If theme is not available. 

90 

91 """ 

92 

93 @abstractmethod 

94 def launch_tui(self, session_config: SessionConfig, tui_config: TUIConfig) -> int: 

95 """Launch the TUI interface with given configuration. 

96 

97 Args: 

98 session_config: Session configuration. 

99 tui_config: TUI configuration. 

100 

101 Returns: 

102 Exit code (0 for success, non-zero for error). 

103 

104 """ 

105 

106 @abstractmethod 

107 def handle_cli_error(self, error: Exception) -> int: 

108 """Handle CLI-level errors and display appropriate messages. 

109 

110 Args: 

111 error: The exception that occurred. 

112 

113 Returns: 

114 Appropriate exit code. 

115 

116 """ 

117 

118 

119class CommandValidationPort(ABC): 

120 """Port interface for command validation operations. 

121 

122 This port defines the contract for validating command arguments 

123 and checking system capabilities. 

124 """ 

125 

126 @abstractmethod 

127 def validate_write_command_args( 

128 self, 

129 node: str | None, 

130 title: str | None, 

131 word_count_goal: int | None, 

132 time_limit: int | None, 

133 ) -> dict[str, str | int | bool]: 

134 """Validate arguments for the write command. 

135 

136 Args: 

137 node: Optional node UUID. 

138 title: Optional title. 

139 word_count_goal: Optional word count goal. 

140 time_limit: Optional time limit. 

141 

142 Returns: 

143 Dictionary of validation results and normalized values. 

144 

145 Raises: 

146 ValidationError: If validation fails. 

147 

148 """ 

149 

150 @abstractmethod 

151 def get_available_themes(self) -> list[str]: 

152 """Get list of available UI themes. 

153 

154 Returns: 

155 List of theme names. 

156 

157 """ 

158 

159 @abstractmethod 

160 def get_current_working_directory(self) -> str: 

161 """Get current working directory. 

162 

163 Returns: 

164 Absolute path to current directory. 

165 

166 """ 

167 

168 @abstractmethod 

169 def check_directory_writable(self, directory: str) -> bool: 

170 """Check if directory is writable. 

171 

172 Args: 

173 directory: Directory path to check. 

174 

175 Returns: 

176 True if writable, False otherwise. 

177 

178 """ 

179 

180 

181# CLI-specific data structures 

182@dataclass(frozen=True) 

183class CLIContext: 

184 """Context information for CLI operations.""" 

185 

186 command_name: str 

187 arguments: dict[str, str | int | bool] 

188 working_directory: str 

189 user_config: dict[str, str] 

190 debug_mode: bool 

191 

192 

193@dataclass(frozen=True) 

194class ValidationResult: 

195 """Result of argument validation.""" 

196 

197 is_valid: bool 

198 errors: list[str] 

199 warnings: list[str] 

200 normalized_values: dict[str, str | int | bool] 

201 

202 

203@dataclass(frozen=True) 

204class CLIResponse: 

205 """Response from CLI operations.""" 

206 

207 exit_code: int 

208 message: str | None 

209 error_details: dict[str, str] | None