Mountain/ApplicationState/DTO/
MarkerDataDTO.rs

1//! # MarkerDataDTO
2//!
3//! # RESPONSIBILITY
4//! - Data transfer object for diagnostic markers (errors, warnings, etc.)
5//! - Serializable format for gRPC/IPC transmission
6//! - Used by Mountain to display diagnostics in the UI
7//!
8//! # FIELDS
9//! - Severity: Marker severity level (Error, Warning, Info, Hint)
10//! - Message: Diagnostic message text
11//! - StartLineNumber/StartColumn: Position start (0-based)
12//! - EndLineNumber/EndColumn: Position end (0-based)
13//! - Source: Diagnostic source (e.g., compiler, linter)
14//! - Code: Diagnostic code for quick fix lookup
15//! - ModelVersionIdentifier: Document version for tracking
16//! - RelatedInformation: Related diagnostic information
17//! - Tags: Additional marker tags (deprecated, unnecessary)
18
19use serde::{Deserialize, Serialize};
20use serde_json::Value;
21
22use super::MarkerSeverity::MarkerSeverity;
23
24/// Maximum message length for a marker
25const MAX_MARKER_MESSAGE_LENGTH:usize = 10_000;
26
27/// Maximum source string length
28const MAX_SOURCE_LENGTH:usize = 256;
29
30/// Represents a single diagnostic marker, such as a compiler error or a linter
31/// warning. This structure is compatible with VS Code's `IMarkerData`
32/// interface and is used by the Diagnostic service.
33#[derive(Serialize, Deserialize, Debug, Clone, Default)]
34#[serde(rename_all = "PascalCase")]
35pub struct MarkerDataDTO {
36	/// Severity level of the marker
37	pub Severity:u32,
38
39	/// Human-readable diagnostic message
40	#[serde(skip_serializing_if = "String::is_empty")]
41	pub Message:String,
42
43	/// Start line number (0-based)
44	pub StartLineNumber:u32,
45
46	/// Start column number (0-based)
47	pub StartColumn:u32,
48
49	/// End line number (0-based)
50	pub EndLineNumber:u32,
51
52	/// End column number (0-based)
53	pub EndColumn:u32,
54
55	/// Diagnostic source (e.g., "typescript", "rustc")
56	#[serde(skip_serializing_if = "Option::is_none")]
57	pub Source:Option<String>,
58
59	/// Diagnostic code for quick fix lookup (string or object)
60	#[serde(skip_serializing_if = "Option::is_none")]
61	pub Code:Option<Value>,
62
63	/// Document version marker is associated with
64	#[serde(skip_serializing_if = "Option::is_none")]
65	pub ModelVersionIdentifier:Option<u64>,
66
67	/// Related diagnostic information
68	#[serde(skip_serializing_if = "Option::is_none")]
69	pub RelatedInformation:Option<Value>,
70
71	/// Additional marker tags (deprecated, unnecessary)
72	#[serde(skip_serializing_if = "Option::is_none")]
73	pub Tags:Option<Vec<u32>>,
74}
75
76impl MarkerDataDTO {
77	/// Creates a new MarkerDataDTO with validation.
78	///
79	/// # Arguments
80	/// * `Severity` - Marker severity level
81	/// * `Message` - Diagnostic message
82	/// * `StartLineNumber` - Start line (0-based)
83	/// * `StartColumn` - Start column (0-based)
84	/// * `EndLineNumber` - End line (0-based)
85	/// * `EndColumn` - End column (0-based)
86	///
87	/// # Returns
88	/// Result containing the DTO or validation error
89	pub fn New(
90		Severity:u32,
91		Message:String,
92		StartLineNumber:u32,
93		StartColumn:u32,
94		EndLineNumber:u32,
95		EndColumn:u32,
96	) -> Result<Self, String> {
97		// Validate severity range
98		if Severity > 8 || Severity == 0 {
99			return Err("Invalid severity value: must be 1, 2, 4, or 8".to_string());
100		}
101
102		// Validate message length
103		if Message.len() > MAX_MARKER_MESSAGE_LENGTH {
104			return Err(format!("Message exceeds maximum length of {} bytes", MAX_MARKER_MESSAGE_LENGTH));
105		}
106
107		// Validate position consistency
108		if StartLineNumber > EndLineNumber {
109			return Err("Start line number cannot be greater than end line number".to_string());
110		}
111
112		// Validate column consistency within same line
113		if StartLineNumber == EndLineNumber && StartColumn > EndColumn {
114			return Err("Start column cannot be greater than end column on the same line".to_string());
115		}
116
117		Ok(Self {
118			Severity,
119			Message,
120			StartLineNumber,
121			StartColumn,
122			EndLineNumber,
123			EndColumn,
124			Source:None,
125			Code:None,
126			ModelVersionIdentifier:None,
127			RelatedInformation:None,
128			Tags:None,
129		})
130	}
131
132	/// Validates the marker's position data.
133	///
134	/// # Returns
135	/// Result indicating valid position or error with reason
136	pub fn ValidatePosition(&self) -> Result<(), String> {
137		if self.StartLineNumber > self.EndLineNumber {
138			return Err("Start line number cannot be greater than end line number".to_string());
139		}
140
141		if self.StartLineNumber == self.EndLineNumber && self.StartColumn > self.EndColumn {
142			return Err("Start column cannot be greater than end column on the same line".to_string());
143		}
144
145		Ok(())
146	}
147
148	/// Sets the source with length validation.
149	///
150	/// # Arguments
151	/// * `Source` - Diagnostic source string
152	///
153	/// # Returns
154	/// Result indicating success or error if source too long
155	pub fn SetSource(&mut self, Source:String) -> Result<(), String> {
156		if Source.len() > MAX_SOURCE_LENGTH {
157			return Err(format!("Source exceeds maximum length of {} bytes", MAX_SOURCE_LENGTH));
158		}
159
160		self.Source = Some(Source);
161		Ok(())
162	}
163
164	/// Gets the severity as a MarkerSeverity enum if valid.
165	///
166	/// # Returns
167	/// Option containing MarkerSeverity or None if invalid
168	pub fn GetSeverity(&self) -> Option<MarkerSeverity> {
169		match self.Severity {
170			8 => Some(MarkerSeverity::Error),
171			4 => Some(MarkerSeverity::Warning),
172			2 => Some(MarkerSeverity::Information),
173			1 => Some(MarkerSeverity::Hint),
174			_ => None,
175		}
176	}
177
178	/// Creates a simple error marker.
179	///
180	/// # Arguments
181	/// * `Message` - Error message
182	/// * `LineNumber` - Line number (0-based)
183	/// * `Column` - Column number (0-based)
184	///
185	/// # Returns
186	/// New MarkerDataDTO configured as an error
187	pub fn Error(Message:String, LineNumber:u32, Column:u32) -> Self {
188		Self {
189			Severity:MarkerSeverity::Error as u32,
190			Message,
191			StartLineNumber:LineNumber,
192			StartColumn:Column,
193			EndLineNumber:LineNumber,
194			EndColumn:Column,
195			..Default::default()
196		}
197	}
198
199	/// Creates a simple warning marker.
200	///
201	/// # Arguments
202	/// * `Message` - Warning message
203	/// * `LineNumber` - Line number (0-based)
204	/// * `Column` - Column number (0-based)
205	///
206	/// # Returns
207	/// New MarkerDataDTO configured as a warning
208	pub fn Warning(Message:String, LineNumber:u32, Column:u32) -> Self {
209		Self {
210			Severity:MarkerSeverity::Warning as u32,
211			Message,
212			StartLineNumber:LineNumber,
213			StartColumn:Column,
214			EndLineNumber:LineNumber,
215			EndColumn:Column,
216			..Default::default()
217		}
218	}
219}