Skip to main content

reifydb_type/
fragment.rs

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