Skip to main content

reifydb_type/
fragment.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2025 ReifyDB
3
4use std::{
5	cmp::Ordering,
6	fmt::{Display, Formatter},
7	ops::Deref,
8	sync::Arc,
9};
10
11use serde::{Deserialize, Serialize};
12
13// Position types for fragments
14#[repr(transparent)]
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
16pub struct StatementColumn(pub u32);
17
18impl Deref for StatementColumn {
19	type Target = u32;
20
21	fn deref(&self) -> &Self::Target {
22		&self.0
23	}
24}
25
26impl PartialEq<i32> for StatementColumn {
27	fn eq(&self, other: &i32) -> bool {
28		self.0 == *other as u32
29	}
30}
31
32#[repr(transparent)]
33#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
34pub struct StatementLine(pub u32);
35
36impl Deref for StatementLine {
37	type Target = u32;
38
39	fn deref(&self) -> &Self::Target {
40		&self.0
41	}
42}
43
44impl PartialEq<i32> for StatementLine {
45	fn eq(&self, other: &i32) -> bool {
46		self.0 == *other as u32
47	}
48}
49
50/// Fragment - owns all its data
51#[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize)]
52pub enum Fragment {
53	/// No fragment information available
54	None,
55
56	/// Fragment from a RQL statement with position information
57	Statement {
58		text: Arc<str>,
59		line: StatementLine,
60		column: StatementColumn,
61	},
62
63	/// Fragment from internal/runtime code
64	Internal {
65		text: Arc<str>,
66	},
67}
68
69impl Fragment {
70	/// Get the text value of the fragment
71	pub fn text(&self) -> &str {
72		match self {
73			Fragment::None => "",
74			Fragment::Statement {
75				text,
76				..
77			}
78			| Fragment::Internal {
79				text,
80				..
81			} => text,
82		}
83	}
84
85	/// Get line position
86	pub fn line(&self) -> StatementLine {
87		match self {
88			Fragment::Statement {
89				line,
90				..
91			} => *line,
92			_ => StatementLine(1),
93		}
94	}
95
96	/// Get column position
97	pub fn column(&self) -> StatementColumn {
98		match self {
99			Fragment::Statement {
100				column,
101				..
102			} => *column,
103			_ => StatementColumn(0),
104		}
105	}
106
107	/// Get a sub-fragment starting at the given offset with the given
108	/// length
109	pub fn sub_fragment(&self, offset: usize, length: usize) -> Fragment {
110		let text = self.text();
111		let end = std::cmp::min(offset + length, text.len());
112		let sub_text = if offset < text.len() {
113			&text[offset..end]
114		} else {
115			""
116		};
117
118		match self {
119			Fragment::None => Fragment::None,
120			Fragment::Statement {
121				line,
122				column,
123				..
124			} => Fragment::Statement {
125				text: Arc::from(sub_text),
126				line: *line,
127				column: StatementColumn(column.0 + offset as u32),
128			},
129			Fragment::Internal {
130				..
131			} => Fragment::Internal {
132				text: Arc::from(sub_text),
133			},
134		}
135	}
136
137	/// Return a new fragment with replaced text, preserving location info.
138	pub fn with_text(&self, text: impl Into<String>) -> Fragment {
139		let text = Arc::from(text.into());
140		match self {
141			Fragment::Statement {
142				line,
143				column,
144				..
145			} => Fragment::Statement {
146				text,
147				line: *line,
148				column: *column,
149			},
150			Fragment::Internal {
151				..
152			} => Fragment::Internal {
153				text,
154			},
155			Fragment::None => Fragment::Internal {
156				text,
157			},
158		}
159	}
160}
161
162impl Fragment {
163	/// Create an internal fragment - useful for creating fragments from
164	/// substrings
165	pub fn internal(text: impl Into<String>) -> Self {
166		Fragment::Internal {
167			text: Arc::from(text.into()),
168		}
169	}
170
171	/// Create a testing fragment - returns a Statement fragment for test
172	/// purposes
173	pub fn testing(text: impl Into<String>) -> Self {
174		Fragment::Statement {
175			text: Arc::from(text.into()),
176			line: StatementLine(1),
177			column: StatementColumn(0),
178		}
179	}
180
181	/// Create an empty testing fragment
182	pub fn testing_empty() -> Self {
183		Self::testing("")
184	}
185
186	/// Merge multiple fragments (in any order) into one encompassing
187	/// fragment
188	pub fn merge_all(fragments: impl IntoIterator<Item = Fragment>) -> Fragment {
189		let mut fragments: Vec<Fragment> = fragments.into_iter().collect();
190		assert!(!fragments.is_empty());
191
192		fragments.sort();
193
194		let first = fragments.first().unwrap();
195
196		let mut text = String::with_capacity(fragments.iter().map(|f| f.text().len()).sum());
197		for fragment in &fragments {
198			text.push_str(fragment.text());
199		}
200
201		match first {
202			Fragment::None => Fragment::None,
203			Fragment::Statement {
204				line,
205				column,
206				..
207			} => Fragment::Statement {
208				text: Arc::from(text),
209				line: *line,
210				column: *column,
211			},
212			Fragment::Internal {
213				..
214			} => Fragment::Internal {
215				text: Arc::from(text),
216			},
217		}
218	}
219
220	/// Compatibility: expose fragment field for Fragment compatibility
221	pub fn fragment(&self) -> &str {
222		self.text()
223	}
224}
225
226impl Default for Fragment {
227	fn default() -> Self {
228		Fragment::None
229	}
230}
231
232impl AsRef<str> for Fragment {
233	fn as_ref(&self) -> &str {
234		self.text()
235	}
236}
237
238impl Display for Fragment {
239	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
240		Display::fmt(self.text(), f)
241	}
242}
243
244impl PartialOrd for Fragment {
245	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
246		Some(self.cmp(other))
247	}
248}
249
250impl Ord for Fragment {
251	fn cmp(&self, other: &Self) -> Ordering {
252		self.column().cmp(&other.column()).then(self.line().cmp(&other.line()))
253	}
254}
255
256impl Eq for Fragment {}
257
258// Convenience From implementations
259impl From<String> for Fragment {
260	fn from(s: String) -> Self {
261		Fragment::Internal {
262			text: Arc::from(s),
263		}
264	}
265}
266
267impl From<&str> for Fragment {
268	fn from(s: &str) -> Self {
269		Fragment::Internal {
270			text: Arc::from(s),
271		}
272	}
273}
274
275impl Fragment {
276	/// Create a statement fragment with position info
277	pub fn statement(text: impl Into<String>, line: u32, column: u32) -> Self {
278		Fragment::Statement {
279			text: Arc::from(text.into()),
280			line: StatementLine(line),
281			column: StatementColumn(column),
282		}
283	}
284
285	/// Create a none fragment
286	pub fn none() -> Self {
287		Fragment::None
288	}
289}
290
291// PartialEq implementations for Fragment with str/String
292impl PartialEq<str> for Fragment {
293	fn eq(&self, other: &str) -> bool {
294		self.text() == other
295	}
296}
297
298impl PartialEq<&str> for Fragment {
299	fn eq(&self, other: &&str) -> bool {
300		self.text() == *other
301	}
302}
303
304impl PartialEq<String> for Fragment {
305	fn eq(&self, other: &String) -> bool {
306		self.text() == other.as_str()
307	}
308}
309
310impl PartialEq<String> for &Fragment {
311	fn eq(&self, other: &String) -> bool {
312		self.text() == other.as_str()
313	}
314}
315
316/// Trait for types that can lazily provide a Fragment
317pub trait LazyFragment {
318	fn fragment(&self) -> Fragment;
319}
320
321impl<F> LazyFragment for F
322where
323	F: Fn() -> Fragment,
324{
325	fn fragment(&self) -> Fragment {
326		self()
327	}
328}
329
330impl LazyFragment for &Fragment {
331	fn fragment(&self) -> Fragment {
332		(*self).clone()
333	}
334}
335
336impl LazyFragment for Fragment {
337	fn fragment(&self) -> Fragment {
338		self.clone()
339	}
340}