reifydb_type/
fragment.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the MIT, see license.md file
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<String>,
59		line: StatementLine,
60		column: StatementColumn,
61	},
62
63	/// Fragment from internal/runtime code
64	Internal {
65		text: Arc<String>,
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.to_string()),
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.to_string()),
133			},
134		}
135	}
136}
137
138impl Fragment {
139	/// Create an internal fragment - useful for creating fragments from
140	/// substrings
141	pub fn internal(text: impl Into<String>) -> Self {
142		Fragment::Internal {
143			text: Arc::from(text.into()),
144		}
145	}
146
147	/// Create a testing fragment - returns a Statement fragment for test
148	/// purposes
149	pub fn testing(text: impl Into<String>) -> Self {
150		Fragment::Statement {
151			text: Arc::from(text.into()),
152			line: StatementLine(1),
153			column: StatementColumn(0),
154		}
155	}
156
157	/// Create an empty testing fragment
158	pub fn testing_empty() -> Self {
159		Self::testing("")
160	}
161
162	/// Merge multiple fragments (in any order) into one encompassing
163	/// fragment
164	pub fn merge_all(fragments: impl IntoIterator<Item = Fragment>) -> Fragment {
165		let mut fragments: Vec<Fragment> = fragments.into_iter().collect();
166		assert!(!fragments.is_empty());
167
168		fragments.sort();
169
170		let first = fragments.first().unwrap();
171
172		let mut text = String::with_capacity(fragments.iter().map(|f| f.text().len()).sum());
173		for fragment in &fragments {
174			text.push_str(fragment.text());
175		}
176
177		match first {
178			Fragment::None => Fragment::None,
179			Fragment::Statement {
180				line,
181				column,
182				..
183			} => Fragment::Statement {
184				text: Arc::from(text),
185				line: *line,
186				column: *column,
187			},
188			Fragment::Internal {
189				..
190			} => Fragment::Internal {
191				text: Arc::from(text),
192			},
193		}
194	}
195
196	/// Compatibility: expose fragment field for Fragment compatibility
197	pub fn fragment(&self) -> &str {
198		self.text()
199	}
200}
201
202impl Default for Fragment {
203	fn default() -> Self {
204		Fragment::None
205	}
206}
207
208impl AsRef<str> for Fragment {
209	fn as_ref(&self) -> &str {
210		self.text()
211	}
212}
213
214impl Display for Fragment {
215	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
216		Display::fmt(self.text(), f)
217	}
218}
219
220impl PartialOrd for Fragment {
221	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
222		Some(self.cmp(other))
223	}
224}
225
226impl Ord for Fragment {
227	fn cmp(&self, other: &Self) -> Ordering {
228		self.column().cmp(&other.column()).then(self.line().cmp(&other.line()))
229	}
230}
231
232impl Eq for Fragment {}
233
234// Convenience From implementations
235impl From<String> for Fragment {
236	fn from(s: String) -> Self {
237		Fragment::Internal {
238			text: Arc::from(s),
239		}
240	}
241}
242
243impl From<&str> for Fragment {
244	fn from(s: &str) -> Self {
245		Fragment::Internal {
246			text: Arc::from(s.to_string()),
247		}
248	}
249}
250
251impl Fragment {
252	/// Create a statement fragment with position info
253	pub fn statement(text: impl Into<String>, line: u32, column: u32) -> Self {
254		Fragment::Statement {
255			text: Arc::from(text.into()),
256			line: StatementLine(line),
257			column: StatementColumn(column),
258		}
259	}
260
261	/// Create a none fragment
262	pub fn none() -> Self {
263		Fragment::None
264	}
265}
266
267// PartialEq implementations for Fragment with str/String
268impl PartialEq<str> for Fragment {
269	fn eq(&self, other: &str) -> bool {
270		self.text() == other
271	}
272}
273
274impl PartialEq<&str> for Fragment {
275	fn eq(&self, other: &&str) -> bool {
276		self.text() == *other
277	}
278}
279
280impl PartialEq<String> for Fragment {
281	fn eq(&self, other: &String) -> bool {
282		self.text() == other.as_str()
283	}
284}
285
286impl PartialEq<String> for &Fragment {
287	fn eq(&self, other: &String) -> bool {
288		self.text() == other.as_str()
289	}
290}
291
292/// Trait for types that can lazily provide a Fragment
293pub trait LazyFragment {
294	fn fragment(&self) -> Fragment;
295}
296
297impl<F> LazyFragment for F
298where
299	F: Fn() -> Fragment,
300{
301	fn fragment(&self) -> Fragment {
302		self()
303	}
304}
305
306impl LazyFragment for &Fragment {
307	fn fragment(&self) -> Fragment {
308		(*self).clone()
309	}
310}
311
312impl LazyFragment for Fragment {
313	fn fragment(&self) -> Fragment {
314		self.clone()
315	}
316}