1use alloc::{
2 boxed::Box,
3 collections::BTreeMap,
4 string::{String, ToString},
5 sync::Arc,
6 vec::Vec,
7};
8use core::{error::Error, fmt::Debug};
9
10use super::*;
11
12#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct SourceId(u32);
19
20impl Default for SourceId {
21 fn default() -> Self {
22 Self::UNKNOWN
23 }
24}
25
26impl SourceId {
27 pub const UNKNOWN: Self = Self(u32::MAX);
28
29 pub fn new(id: u32) -> Self {
31 assert_ne!(id, u32::MAX, "u32::MAX is a reserved value for SourceId::default()/UNKNOWN");
32
33 Self(id)
34 }
35
36 #[inline(always)]
38 pub const fn new_unchecked(id: u32) -> Self {
39 Self(id)
40 }
41
42 #[inline(always)]
43 pub const fn to_usize(self) -> usize {
44 self.0 as usize
45 }
46
47 #[inline(always)]
48 pub const fn to_u32(self) -> u32 {
49 self.0
50 }
51
52 pub const fn is_unknown(&self) -> bool {
53 self.0 == u32::MAX
54 }
55}
56
57impl TryFrom<usize> for SourceId {
58 type Error = ();
59
60 #[inline]
61 fn try_from(id: usize) -> Result<Self, Self::Error> {
62 match u32::try_from(id) {
63 Ok(n) if n < u32::MAX => Ok(Self(n)),
64 _ => Err(()),
65 }
66 }
67}
68
69#[derive(Debug, thiserror::Error)]
74pub enum SourceManagerError {
75 #[error("attempted to use an invalid source id")]
78 InvalidSourceId,
79 #[error("attempted to read content out of bounds")]
81 InvalidBounds,
82 #[error("{error_msg}")]
84 Custom {
85 error_msg: Box<str>,
86 source: Option<Box<dyn Error + Send + Sync + 'static>>,
88 },
89}
90
91impl SourceManagerError {
92 pub fn custom(message: String) -> Self {
93 Self::Custom { error_msg: message.into(), source: None }
94 }
95
96 pub fn custom_with_source(message: String, source: impl Error + Send + Sync + 'static) -> Self {
97 Self::Custom {
98 error_msg: message.into(),
99 source: Some(Box::new(source)),
100 }
101 }
102}
103
104pub trait SourceManager: Debug {
105 fn is_manager_of(&self, file: &SourceFile) -> bool {
107 match self.get(file.id()) {
108 Ok(found) => core::ptr::addr_eq(Arc::as_ptr(&found), file),
109 Err(_) => false,
110 }
111 }
112 fn copy_into(&self, file: &SourceFile) -> Arc<SourceFile> {
116 if let Ok(found) = self.get(file.id()) {
117 if core::ptr::addr_eq(Arc::as_ptr(&found), file) {
118 return found;
119 }
120 }
121 self.load_from_raw_parts(file.name(), file.content().clone())
122 }
123 fn load(&self, name: &str, content: String) -> Arc<SourceFile> {
125 let name = Arc::from(name.to_string().into_boxed_str());
126 let content = SourceContent::new(Arc::clone(&name), content.into_boxed_str());
127 self.load_from_raw_parts(name, content)
128 }
129 fn load_from_raw_parts(&self, name: Arc<str>, content: SourceContent) -> Arc<SourceFile>;
131 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError>;
133 fn get_by_path(&self, path: &str) -> Option<Arc<SourceFile>> {
135 self.find(path).and_then(|id| self.get(id).ok())
136 }
137 fn find(&self, name: &str) -> Option<SourceId>;
139 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan>;
141 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError>;
143 fn location_to_span(&self, loc: Location) -> Option<SourceSpan>;
145 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError>;
147 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError>;
149 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError>;
151}
152
153impl<T: ?Sized + SourceManager> SourceManager for Arc<T> {
154 #[inline(always)]
155 fn is_manager_of(&self, file: &SourceFile) -> bool {
156 (**self).is_manager_of(file)
157 }
158 #[inline(always)]
159 fn copy_into(&self, file: &SourceFile) -> Arc<SourceFile> {
160 (**self).copy_into(file)
161 }
162 #[inline(always)]
163 fn load(&self, name: &str, content: String) -> Arc<SourceFile> {
164 (**self).load(name, content)
165 }
166 #[inline(always)]
167 fn load_from_raw_parts(&self, name: Arc<str>, content: SourceContent) -> Arc<SourceFile> {
168 (**self).load_from_raw_parts(name, content)
169 }
170 #[inline(always)]
171 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
172 (**self).get(id)
173 }
174 #[inline(always)]
175 fn get_by_path(&self, path: &str) -> Option<Arc<SourceFile>> {
176 (**self).get_by_path(path)
177 }
178 #[inline(always)]
179 fn find(&self, name: &str) -> Option<SourceId> {
180 (**self).find(name)
181 }
182 #[inline(always)]
183 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
184 (**self).file_line_col_to_span(loc)
185 }
186 #[inline(always)]
187 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
188 (**self).file_line_col(span)
189 }
190 #[inline(always)]
191 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
192 (**self).location_to_span(loc)
193 }
194 #[inline(always)]
195 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
196 (**self).location(span)
197 }
198 #[inline(always)]
199 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
200 (**self).source(id)
201 }
202 #[inline(always)]
203 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
204 (**self).source_slice(span)
205 }
206}
207
208#[cfg(feature = "std")]
209pub trait SourceManagerExt: SourceManager {
210 fn load_file(&self, path: &std::path::Path) -> Result<Arc<SourceFile>, SourceManagerError> {
213 let name = path.to_string_lossy();
214 if let Some(existing) = self.get_by_path(name.as_ref()) {
215 return Ok(existing);
216 }
217
218 let name = Arc::from(name.into_owned().into_boxed_str());
219 let content = std::fs::read_to_string(path)
220 .map(|s| SourceContent::new(Arc::clone(&name), s.into_boxed_str()))
221 .map_err(|source| {
222 SourceManagerError::custom_with_source(
223 format!("failed to load filed at `{}`", path.display()),
224 source,
225 )
226 })?;
227
228 Ok(self.load_from_raw_parts(name, content))
229 }
230}
231
232#[cfg(feature = "std")]
233impl<T: ?Sized + SourceManager> SourceManagerExt for T {}
234
235use crate::utils::sync::RwLock;
239
240#[derive(Debug, Default)]
241pub struct DefaultSourceManager(RwLock<DefaultSourceManagerImpl>);
242impl Clone for DefaultSourceManager {
243 fn clone(&self) -> Self {
244 let manager = self.0.read();
245 Self(RwLock::new(manager.clone()))
246 }
247}
248
249#[derive(Debug, Default, Clone)]
250struct DefaultSourceManagerImpl {
251 files: Vec<Arc<SourceFile>>,
252 names: BTreeMap<Arc<str>, SourceId>,
253}
254
255impl DefaultSourceManagerImpl {
256 fn insert(&mut self, name: Arc<str>, content: SourceContent) -> Arc<SourceFile> {
257 if let Some(file) = self.names.get(&name).copied().and_then(|id| {
260 let file = &self.files[id.to_usize()];
261 if file.as_str() == content.as_str() {
262 Some(Arc::clone(file))
263 } else {
264 None
265 }
266 }) {
267 return file;
268 }
269 let id = SourceId::try_from(self.files.len())
270 .expect("system limit: source manager has exhausted its supply of source ids");
271 let file = Arc::new(SourceFile::from_raw_parts(id, content));
272 self.files.push(Arc::clone(&file));
273 self.names.insert(Arc::clone(&name), id);
274 file
275 }
276
277 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
278 self.files
279 .get(id.to_usize())
280 .cloned()
281 .ok_or(SourceManagerError::InvalidSourceId)
282 }
283
284 fn get_by_path(&self, path: &str) -> Option<Arc<SourceFile>> {
285 self.find(path).and_then(|id| self.get(id).ok())
286 }
287
288 fn find(&self, name: &str) -> Option<SourceId> {
289 self.names.get(name).copied()
290 }
291
292 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
293 let file = self
294 .names
295 .get(&loc.path)
296 .copied()
297 .and_then(|id| self.files.get(id.to_usize()))?;
298 file.line_column_to_span(loc.line, loc.column)
299 }
300
301 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
302 self.files
303 .get(span.source_id().to_usize())
304 .ok_or(SourceManagerError::InvalidSourceId)
305 .map(|file| file.location(span))
306 }
307
308 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
309 let file = self
310 .names
311 .get(&loc.path)
312 .copied()
313 .and_then(|id| self.files.get(id.to_usize()))?;
314
315 let max_len = ByteIndex::from(file.as_str().len() as u32);
316 if loc.start >= max_len || loc.end > max_len {
317 return None;
318 }
319
320 Some(SourceSpan::new(file.id(), loc.start..loc.end))
321 }
322
323 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
324 self.files
325 .get(span.source_id().to_usize())
326 .ok_or(SourceManagerError::InvalidSourceId)
327 .map(|file| Location::new(file.name(), span.start(), span.end()))
328 }
329}
330
331impl SourceManager for DefaultSourceManager {
332 fn load_from_raw_parts(&self, name: Arc<str>, content: SourceContent) -> Arc<SourceFile> {
333 let mut manager = self.0.write();
334 manager.insert(name, content)
335 }
336
337 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
338 let manager = self.0.read();
339 manager.get(id)
340 }
341
342 fn get_by_path(&self, path: &str) -> Option<Arc<SourceFile>> {
343 let manager = self.0.read();
344 manager.get_by_path(path)
345 }
346
347 fn find(&self, name: &str) -> Option<SourceId> {
348 let manager = self.0.read();
349 manager.find(name)
350 }
351
352 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
353 let manager = self.0.read();
354 manager.file_line_col_to_span(loc)
355 }
356
357 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
358 let manager = self.0.read();
359 manager.file_line_col(span)
360 }
361
362 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
363 let manager = self.0.read();
364 manager.location_to_span(loc)
365 }
366
367 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
368 let manager = self.0.read();
369 manager.location(span)
370 }
371
372 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
373 let manager = self.0.read();
374 let ptr = manager
375 .files
376 .get(id.to_usize())
377 .ok_or(SourceManagerError::InvalidSourceId)
378 .map(|file| file.as_str() as *const str)?;
379 drop(manager);
380 Ok(unsafe { &*ptr })
385 }
386
387 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
388 self.source(span.source_id())?
389 .get(span.into_slice_index())
390 .ok_or(SourceManagerError::InvalidBounds)
391 }
392}
393
394#[cfg(test)]
395mod error_assertions {
396 use super::*;
397
398 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
400
401 fn _assert_source_manager_error_bounds(err: SourceManagerError) {
402 _assert_error_is_send_sync_static(err);
403 }
404}