Skip to main content

reifydb_type/
fragment.rs

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