gglib_core/ports/
mcp_dto.rs1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14pub struct ResolutionStatus {
15 pub success: bool,
17
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub resolved_path: Option<String>,
21
22 #[serde(skip_serializing_if = "Vec::is_empty", default)]
24 pub attempts: Vec<ResolutionAttempt>,
25
26 #[serde(skip_serializing_if = "Vec::is_empty", default)]
28 pub warnings: Vec<String>,
29
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub error_message: Option<String>,
33
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub suggested_fix: Option<String>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41#[serde(rename_all = "snake_case")]
42pub struct ResolutionAttempt {
43 pub candidate: String,
45
46 pub outcome: String,
48}
49
50impl ResolutionStatus {
51 pub fn error_message_with_suggestions(&self) -> String {
53 let base_msg = self
54 .error_message
55 .clone()
56 .unwrap_or_else(|| "Resolution failed".to_string());
57
58 if self.attempts.is_empty() {
59 return base_msg;
60 }
61
62 let attempts_list: Vec<String> = self
63 .attempts
64 .iter()
65 .map(|a| format!(" ✗ {}: {}", a.candidate, a.outcome))
66 .collect();
67
68 let mut msg = format!("{}\n\nTried:\n{}", base_msg, attempts_list.join("\n"));
69
70 if let Some(fix) = &self.suggested_fix {
71 msg.push_str("\n\nSuggested fix: ");
72 msg.push_str(fix);
73 }
74
75 let all_not_found = self
77 .attempts
78 .iter()
79 .all(|a| a.outcome.contains("not found"));
80 if all_not_found {
81 msg.push_str("\n\n• Install Node.js if not installed");
82 }
83
84 msg
85 }
86}