1use crate::system::error::{DoumError, DoumResult};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct ModeSelectResponse {
7 pub mode: String,
8 pub reason: String,
9}
10
11pub type AskResponse = String;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct CommandSuggestion {
17 pub cmd: String,
18 pub description: String,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct SuggestResponse {
24 pub suggestions: Vec<CommandSuggestion>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct ExecuteResponse {
30 pub command: String,
31 pub description: String,
32 pub is_dangerous: bool,
33}
34
35pub fn parse_mode_select(json_str: &str) -> DoumResult<ModeSelectResponse> {
37 let cleaned = extract_json(json_str);
39
40 serde_json::from_str(&cleaned)
41 .map_err(|e| DoumError::Parse(format!("모드 선택 응답 파싱 실패: {}", e)))
42}
43
44pub fn parse_suggest(json_str: &str) -> DoumResult<SuggestResponse> {
46 let cleaned = extract_json(json_str);
47
48 serde_json::from_str(&cleaned)
49 .map_err(|e| DoumError::Parse(format!("Suggest 응답 파싱 실패: {}", e)))
50}
51
52pub fn parse_execute(json_str: &str) -> DoumResult<ExecuteResponse> {
54 let cleaned = extract_json(json_str);
55
56 serde_json::from_str(&cleaned)
57 .map_err(|e| DoumError::Parse(format!("Execute 응답 파싱 실패: {}", e)))
58}
59
60fn extract_json(text: &str) -> String {
62 let text = text.trim();
63
64 if let Some(start) = text.find("```")
66 && let Some(end) = text[start + 3..].find("```")
67 {
68 let json_block = &text[start + 3..start + 3 + end];
69 let json_content = if let Some(newline) = json_block.find('\n') {
71 &json_block[newline + 1..]
72 } else {
73 json_block
74 };
75 return json_content.trim().to_string();
76 }
77
78 if let Some(start) = text.find('{')
80 && let Some(end) = text.rfind('}')
81 && end > start
82 {
83 return text[start..=end].to_string();
84 }
85
86 text.to_string()
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_parse_mode_select() {
96 let json = r#"{"mode":"ask","reason":"사용자가 질문을 했습니다"}"#;
97 let result = parse_mode_select(json).unwrap();
98 assert_eq!(result.mode, "ask");
99 assert_eq!(result.reason, "사용자가 질문을 했습니다");
100 }
101
102 #[test]
103 fn test_parse_mode_select_with_markdown() {
104 let json = r#"
105```json
106{"mode":"execute","reason":"실행 요청"}
107```
108 "#;
109 let result = parse_mode_select(json).unwrap();
110 assert_eq!(result.mode, "execute");
111 }
112
113 #[test]
114 fn test_parse_suggest() {
115 let json = r#"
116{
117 "suggestions": [
118 {"cmd": "ls -la", "description": "모든 파일 나열"},
119 {"cmd": "dir", "description": "디렉터리 내용 보기"}
120 ]
121}
122 "#;
123 let result = parse_suggest(json).unwrap();
124 assert_eq!(result.suggestions.len(), 2);
125 assert_eq!(result.suggestions[0].cmd, "ls -la");
126 }
127
128 #[test]
129 fn test_parse_execute() {
130 let json = r#"
131{
132 "command": "echo hello",
133 "description": "hello 출력",
134 "is_dangerous": false
135}
136 "#;
137 let result = parse_execute(json).unwrap();
138 assert_eq!(result.command, "echo hello");
139 assert!(!result.is_dangerous);
140 }
141
142 #[test]
143 fn test_parse_execute_dangerous() {
144 let json = r#"{"command":"rm -rf /","description":"위험","is_dangerous":true}"#;
145 let result = parse_execute(json).unwrap();
146 assert!(result.is_dangerous);
147 }
148
149 #[test]
150 fn test_extract_json() {
151 let text = "Some text before\n{\"key\":\"value\"}\nSome text after";
152 let result = extract_json(text);
153 assert_eq!(result, r#"{"key":"value"}"#);
154 }
155
156 #[test]
157 fn test_extract_json_with_code_block() {
158 let text = "```json\n{\"key\":\"value\"}\n```";
159 let result = extract_json(text);
160 assert_eq!(result, r#"{"key":"value"}"#);
161 }
162}