1use crate::{GateId, LawAxis, ReceiptObligation};
2use lsp_types_max::{CodeAction, Diagnostic};
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Default, Serialize, Deserialize)]
10pub enum Repairability {
11 Repairable,
12 NotRepairable,
13 #[default]
14 Unknown,
15}
16
17#[derive(Debug, Clone, Default, Serialize, Deserialize)]
18pub enum Terminality {
19 Terminal,
20 #[default]
21 NonTerminal,
22}
23
24impl std::fmt::Display for Terminality {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 write!(f, "{:?}", self)
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct TransitionAttempt {
32 pub from_state: String,
33 pub to_state: String,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct DocRoute {
38 pub path: String,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct RepairAction {
43 pub action_id: String,
44 pub description: String,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct MaxDiagnostic {
53 pub lsp: Diagnostic,
54 pub diagnostic_id: String,
55 pub law_id: String,
56 pub attempted_transition: Option<TransitionAttempt>,
57 pub violated_axes: Vec<String>,
58 pub doc_routes: Vec<DocRoute>,
59 pub repair_actions: Vec<RepairAction>,
60 pub verification_gates: Vec<GateId>,
61 pub receipt_obligation: Option<ReceiptObligation>,
62
63 #[serde(default)]
65 pub law_axis: LawAxis,
66 #[serde(default)]
67 pub violated_invariant: String,
68 #[serde(default)]
69 pub observed_state: serde_json::Value,
70 #[serde(default)]
71 pub expected_state: serde_json::Value,
72 #[serde(default)]
73 pub repairability: Repairability,
74 #[serde(default)]
75 pub terminality: Terminality,
76}
77
78impl Default for MaxDiagnostic {
79 fn default() -> Self {
80 Self {
81 lsp: Diagnostic::default(),
82 diagnostic_id: String::new(),
83 law_id: String::new(),
84 attempted_transition: None,
85 violated_axes: Vec::new(),
86 doc_routes: Vec::new(),
87 repair_actions: Vec::new(),
88 verification_gates: Vec::new(),
89 receipt_obligation: None,
90 law_axis: LawAxis::default(),
91 violated_invariant: String::new(),
92 observed_state: serde_json::Value::Null,
93 expected_state: serde_json::Value::Null,
94 repairability: Repairability::default(),
95 terminality: Terminality::default(),
96 }
97 }
98}
99
100impl MaxDiagnostic {
101 pub fn into_lsp(self) -> Diagnostic {
103 self.lsp
104 }
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct Precondition {
109 pub condition: String,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct ValidationPlan {
114 pub gates: Vec<GateId>,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct RollbackPlan {
119 pub strategy: String,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct ReceiptPlan {
124 pub expected_receipts: Vec<String>,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct MaxCodeAction {
129 pub action: CodeAction,
130 pub preconditions: Vec<Precondition>,
131 pub validation_plan: ValidationPlan,
132 pub rollback_plan: RollbackPlan,
133 pub receipt_plan: ReceiptPlan,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct SnapshotId(pub String);
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn max_diagnostic_default_is_valid() {
145 let d = MaxDiagnostic::default();
146 assert!(d.diagnostic_id.is_empty());
147 assert!(d.violated_axes.is_empty());
148 assert!(d.repair_actions.is_empty());
149 }
150
151 #[test]
152 fn max_diagnostic_into_lsp_returns_lsp_field() {
153 let d = MaxDiagnostic {
154 diagnostic_id: "diag-1".to_string(),
155 law_id: "LSP-001".to_string(),
156 ..MaxDiagnostic::default()
157 };
158 let lsp = d.into_lsp();
159 assert_eq!(lsp.message, String::new());
161 }
162}