1use alloc::{
2 boxed::Box,
3 collections::BTreeMap,
4 string::{String, ToString},
5 sync::Arc,
6 vec::Vec,
7};
8use core::error::Error;
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 {
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(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(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 file
274 }
275
276 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
277 self.files
278 .get(id.to_usize())
279 .cloned()
280 .ok_or(SourceManagerError::InvalidSourceId)
281 }
282
283 fn get_by_path(&self, path: &str) -> Option<Arc<SourceFile>> {
284 self.find(path).and_then(|id| self.get(id).ok())
285 }
286
287 fn find(&self, name: &str) -> Option<SourceId> {
288 self.names.get(name).copied()
289 }
290
291 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
292 let file = self
293 .names
294 .get(&loc.path)
295 .copied()
296 .and_then(|id| self.files.get(id.to_usize()))?;
297 file.line_column_to_span(loc.line, loc.column)
298 }
299
300 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
301 self.files
302 .get(span.source_id().to_usize())
303 .ok_or(SourceManagerError::InvalidSourceId)
304 .map(|file| file.location(span))
305 }
306
307 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
308 let file = self
309 .names
310 .get(&loc.path)
311 .copied()
312 .and_then(|id| self.files.get(id.to_usize()))?;
313
314 let max_len = ByteIndex::from(file.as_str().len() as u32);
315 if loc.start >= max_len || loc.end > max_len {
316 return None;
317 }
318
319 Some(SourceSpan::new(file.id(), loc.start..loc.end))
320 }
321
322 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
323 self.files
324 .get(span.source_id().to_usize())
325 .ok_or(SourceManagerError::InvalidSourceId)
326 .map(|file| Location::new(file.name(), span.start(), span.end()))
327 }
328}
329
330impl SourceManager for DefaultSourceManager {
331 fn load_from_raw_parts(&self, name: Arc<str>, content: SourceContent) -> Arc<SourceFile> {
332 let mut manager = self.0.write();
333 manager.insert(name, content)
334 }
335
336 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
337 let manager = self.0.read();
338 manager.get(id)
339 }
340
341 fn get_by_path(&self, path: &str) -> Option<Arc<SourceFile>> {
342 let manager = self.0.read();
343 manager.get_by_path(path)
344 }
345
346 fn find(&self, name: &str) -> Option<SourceId> {
347 let manager = self.0.read();
348 manager.find(name)
349 }
350
351 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
352 let manager = self.0.read();
353 manager.file_line_col_to_span(loc)
354 }
355
356 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
357 let manager = self.0.read();
358 manager.file_line_col(span)
359 }
360
361 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
362 let manager = self.0.read();
363 manager.location_to_span(loc)
364 }
365
366 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
367 let manager = self.0.read();
368 manager.location(span)
369 }
370
371 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
372 let manager = self.0.read();
373 let ptr = manager
374 .files
375 .get(id.to_usize())
376 .ok_or(SourceManagerError::InvalidSourceId)
377 .map(|file| file.as_str() as *const str)?;
378 drop(manager);
379 Ok(unsafe { &*ptr })
384 }
385
386 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
387 self.source(span.source_id())?
388 .get(span.into_slice_index())
389 .ok_or(SourceManagerError::InvalidBounds)
390 }
391}
392
393#[cfg(test)]
394mod error_assertions {
395 use super::*;
396
397 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
399
400 fn _assert_source_manager_error_bounds(err: SourceManagerError) {
401 _assert_error_is_send_sync_static(err);
402 }
403}