1use crate::system::error::{DoumError, Result};
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) -> Result<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) -> Result<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) -> Result<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 let json_block = &text[start + 3..start + 3 + end];
68 let json_content = if let Some(newline) = json_block.find('\n') {
70 &json_block[newline + 1..]
71 } else {
72 json_block
73 };
74 return json_content.trim().to_string();
75 }
76
77 if let Some(start) = text.find('{')
79 && let Some(end) = text.rfind('}')
80 && end > start {
81 return text[start..=end].to_string();
82 }
83
84 text.to_string()
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_parse_mode_select() {
94 let json = r#"{"mode":"ask","reason":"사용자가 질문을 했습니다"}"#;
95 let result = parse_mode_select(json).unwrap();
96 assert_eq!(result.mode, "ask");
97 assert_eq!(result.reason, "사용자가 질문을 했습니다");
98 }
99
100 #[test]
101 fn test_parse_mode_select_with_markdown() {
102 let json = r#"
103```json
104{"mode":"execute","reason":"실행 요청"}
105```
106 "#;
107 let result = parse_mode_select(json).unwrap();
108 assert_eq!(result.mode, "execute");
109 }
110
111 #[test]
112 fn test_parse_suggest() {
113 let json = r#"
114{
115 "suggestions": [
116 {"cmd": "ls -la", "description": "모든 파일 나열"},
117 {"cmd": "dir", "description": "디렉터리 내용 보기"}
118 ]
119}
120 "#;
121 let result = parse_suggest(json).unwrap();
122 assert_eq!(result.suggestions.len(), 2);
123 assert_eq!(result.suggestions[0].cmd, "ls -la");
124 }
125
126 #[test]
127 fn test_parse_execute() {
128 let json = r#"
129{
130 "command": "echo hello",
131 "description": "hello 출력",
132 "is_dangerous": false
133}
134 "#;
135 let result = parse_execute(json).unwrap();
136 assert_eq!(result.command, "echo hello");
137 assert!(!result.is_dangerous);
138 }
139
140 #[test]
141 fn test_parse_execute_dangerous() {
142 let json = r#"{"command":"rm -rf /","description":"위험","is_dangerous":true}"#;
143 let result = parse_execute(json).unwrap();
144 assert!(result.is_dangerous);
145 }
146
147 #[test]
148 fn test_extract_json() {
149 let text = "Some text before\n{\"key\":\"value\"}\nSome text after";
150 let result = extract_json(text);
151 assert_eq!(result, r#"{"key":"value"}"#);
152 }
153
154 #[test]
155 fn test_extract_json_with_code_block() {
156 let text = "```json\n{\"key\":\"value\"}\n```";
157 let result = extract_json(text);
158 assert_eq!(result, r#"{"key":"value"}"#);
159 }
160}