1use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::Arc};
2use core::{error::Error, fmt::Debug};
3
4use miden_utils_indexing::IndexVec;
5
6use super::*;
7
8#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
14#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
15#[cfg_attr(feature = "serde", serde(transparent))]
16pub struct SourceId(u32);
17
18impl From<u32> for SourceId {
19 fn from(value: u32) -> Self {
20 SourceId::new_unchecked(value)
21 }
22}
23
24impl From<SourceId> for u32 {
25 fn from(value: SourceId) -> Self {
26 value.to_u32()
27 }
28}
29
30impl miden_utils_indexing::Idx for SourceId {}
31
32impl Default for SourceId {
33 fn default() -> Self {
34 Self::UNKNOWN
35 }
36}
37
38impl SourceId {
39 pub const UNKNOWN: Self = Self(u32::MAX);
40
41 pub fn new(id: u32) -> Self {
43 assert_ne!(id, u32::MAX, "u32::MAX is a reserved value for SourceId::default()/UNKNOWN");
44
45 Self(id)
46 }
47
48 #[inline(always)]
50 pub const fn new_unchecked(id: u32) -> Self {
51 Self(id)
52 }
53
54 #[inline(always)]
55 pub const fn to_usize(self) -> usize {
56 self.0 as usize
57 }
58
59 #[inline(always)]
60 pub const fn to_u32(self) -> u32 {
61 self.0
62 }
63
64 pub const fn is_unknown(&self) -> bool {
65 self.0 == u32::MAX
66 }
67}
68
69impl TryFrom<usize> for SourceId {
70 type Error = ();
71
72 #[inline]
73 fn try_from(id: usize) -> Result<Self, Self::Error> {
74 match u32::try_from(id) {
75 Ok(n) if n < u32::MAX => Ok(Self(n)),
76 _ => Err(()),
77 }
78 }
79}
80
81#[derive(Debug, thiserror::Error)]
86pub enum SourceManagerError {
87 #[error("attempted to use an invalid source id")]
90 InvalidSourceId,
91 #[error("attempted to read content out of bounds")]
93 InvalidBounds,
94 #[error(transparent)]
95 InvalidContentUpdate(#[from] SourceContentUpdateError),
96 #[error("{error_msg}")]
98 Custom {
99 error_msg: Box<str>,
100 source: Option<Box<dyn Error + Send + Sync + 'static>>,
102 },
103}
104
105impl SourceManagerError {
106 pub fn custom(message: String) -> Self {
107 Self::Custom { error_msg: message.into(), source: None }
108 }
109
110 pub fn custom_with_source(message: String, source: impl Error + Send + Sync + 'static) -> Self {
111 Self::Custom {
112 error_msg: message.into(),
113 source: Some(Box::new(source)),
114 }
115 }
116}
117
118pub trait SourceManager: Debug {
119 fn is_manager_of(&self, file: &SourceFile) -> bool {
121 match self.get(file.id()) {
122 Ok(found) => core::ptr::addr_eq(Arc::as_ptr(&found), file),
123 Err(_) => false,
124 }
125 }
126 fn copy_into(&self, file: &SourceFile) -> Arc<SourceFile> {
130 if let Ok(found) = self.get(file.id())
131 && core::ptr::addr_eq(Arc::as_ptr(&found), file)
132 {
133 return found;
134 }
135 self.load_from_raw_parts(file.uri().clone(), file.content().clone())
136 }
137 fn load(&self, lang: SourceLanguage, name: Uri, content: String) -> Arc<SourceFile> {
139 let content = SourceContent::new(lang, name.clone(), content);
140 self.load_from_raw_parts(name, content)
141 }
142 fn load_from_raw_parts(&self, name: Uri, content: SourceContent) -> Arc<SourceFile>;
144 fn update(
148 &self,
149 id: SourceId,
150 text: String,
151 range: Option<Selection>,
152 version: i32,
153 ) -> Result<(), SourceManagerError>;
154 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError>;
156 fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
158 self.find(uri).and_then(|id| self.get(id).ok())
159 }
160 fn find(&self, uri: &Uri) -> Option<SourceId>;
162 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan>;
164 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError>;
166 fn location_to_span(&self, loc: Location) -> Option<SourceSpan>;
168 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError>;
170 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError>;
172 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError>;
174}
175
176impl<T: ?Sized + SourceManager> SourceManager for Arc<T> {
177 #[inline(always)]
178 fn is_manager_of(&self, file: &SourceFile) -> bool {
179 (**self).is_manager_of(file)
180 }
181 #[inline(always)]
182 fn copy_into(&self, file: &SourceFile) -> Arc<SourceFile> {
183 (**self).copy_into(file)
184 }
185 #[inline(always)]
186 fn load(&self, lang: SourceLanguage, uri: Uri, content: String) -> Arc<SourceFile> {
187 (**self).load(lang, uri, content)
188 }
189 #[inline(always)]
190 fn load_from_raw_parts(&self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
191 (**self).load_from_raw_parts(uri, content)
192 }
193 #[inline(always)]
194 fn update(
195 &self,
196 id: SourceId,
197 text: String,
198 range: Option<Selection>,
199 version: i32,
200 ) -> Result<(), SourceManagerError> {
201 (**self).update(id, text, range, version)
202 }
203 #[inline(always)]
204 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
205 (**self).get(id)
206 }
207 #[inline(always)]
208 fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
209 (**self).get_by_uri(uri)
210 }
211 #[inline(always)]
212 fn find(&self, uri: &Uri) -> Option<SourceId> {
213 (**self).find(uri)
214 }
215 #[inline(always)]
216 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
217 (**self).file_line_col_to_span(loc)
218 }
219 #[inline(always)]
220 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
221 (**self).file_line_col(span)
222 }
223 #[inline(always)]
224 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
225 (**self).location_to_span(loc)
226 }
227 #[inline(always)]
228 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
229 (**self).location(span)
230 }
231 #[inline(always)]
232 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
233 (**self).source(id)
234 }
235 #[inline(always)]
236 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
237 (**self).source_slice(span)
238 }
239}
240
241#[cfg(feature = "std")]
242pub trait SourceManagerExt: SourceManager {
243 fn load_file(&self, path: &std::path::Path) -> Result<Arc<SourceFile>, SourceManagerError> {
245 let uri = Uri::from(path);
246 if let Some(existing) = self.get_by_uri(&uri) {
247 return Ok(existing);
248 }
249
250 let lang = match path.extension().and_then(|ext| ext.to_str()) {
251 Some("masm") => "masm",
252 Some("rs") => "rust",
253 Some(ext) => ext,
254 None => "unknown",
255 };
256
257 let content = std::fs::read_to_string(path)
258 .map(|s| SourceContent::new(lang, uri.clone(), s))
259 .map_err(|source| {
260 SourceManagerError::custom_with_source(
261 alloc::format!("failed to load filed at `{}`", path.display()),
262 source,
263 )
264 })?;
265
266 Ok(self.load_from_raw_parts(uri, content))
267 }
268}
269
270#[cfg(feature = "std")]
271impl<T: ?Sized + SourceManager> SourceManagerExt for T {}
272
273pub trait SourceManagerSync: SourceManager + Send + Sync {}
280
281impl<T: ?Sized + SourceManager + Send + Sync> SourceManagerSync for T {}
282
283use miden_utils_sync::RwLock;
287
288#[derive(Debug, Default)]
289pub struct DefaultSourceManager(RwLock<DefaultSourceManagerImpl>);
290
291impl Default for DefaultSourceManagerImpl {
292 fn default() -> Self {
293 Self::new()
294 }
295}
296impl Clone for DefaultSourceManager {
297 fn clone(&self) -> Self {
298 let manager = self.0.read();
299 Self(RwLock::new(manager.clone()))
300 }
301}
302
303impl Clone for DefaultSourceManagerImpl {
304 fn clone(&self) -> Self {
305 Self {
306 files: self.files.clone(),
307 uris: self.uris.clone(),
308 }
309 }
310}
311
312#[derive(Debug)]
313struct DefaultSourceManagerImpl {
314 files: IndexVec<SourceId, Arc<SourceFile>>,
315 uris: BTreeMap<Uri, SourceId>,
316}
317
318impl DefaultSourceManagerImpl {
319 fn new() -> Self {
320 Self {
321 files: IndexVec::new(),
322 uris: BTreeMap::new(),
323 }
324 }
325
326 fn insert(&mut self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
327 if let Some(file) = self.uris.get(&uri).copied().and_then(|id| {
330 let file = &self.files[id];
331 if file.as_str() == content.as_str() {
332 Some(Arc::clone(file))
333 } else {
334 None
335 }
336 }) {
337 return file;
338 }
339 let id = SourceId::try_from(self.files.len())
340 .expect("system limit: source manager has exhausted its supply of source ids");
341 let file = Arc::new(SourceFile::from_raw_parts(id, content));
342 let file_clone = Arc::clone(&file);
343 self.files
344 .push(file_clone)
345 .expect("system limit: source manager has exhausted its supply of source ids");
346 self.uris.insert(uri.clone(), id);
347 file
348 }
349
350 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
351 self.files.get(id).cloned().ok_or(SourceManagerError::InvalidSourceId)
352 }
353
354 fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
355 self.find(uri).and_then(|id| self.get(id).ok())
356 }
357
358 fn find(&self, uri: &Uri) -> Option<SourceId> {
359 self.uris.get(uri).copied()
360 }
361
362 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
363 let file = self.uris.get(&loc.uri).copied().and_then(|id| self.files.get(id))?;
364 file.line_column_to_span(loc.line, loc.column)
365 }
366
367 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
368 self.files
369 .get(span.source_id())
370 .ok_or(SourceManagerError::InvalidSourceId)
371 .map(|file| file.location(span))
372 }
373
374 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
375 let file = self.uris.get(&loc.uri).copied().and_then(|id| self.files.get(id))?;
376
377 let max_len = ByteIndex::from(file.as_str().len() as u32);
378 if loc.start >= max_len || loc.end > max_len {
379 return None;
380 }
381
382 Some(SourceSpan::new(file.id(), loc.start..loc.end))
383 }
384
385 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
386 self.files
387 .get(span.source_id())
388 .ok_or(SourceManagerError::InvalidSourceId)
389 .map(|file| Location::new(file.uri().clone(), span.start(), span.end()))
390 }
391}
392
393impl SourceManager for DefaultSourceManager {
394 fn load_from_raw_parts(&self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
395 let mut manager = self.0.write();
396 manager.insert(uri, content)
397 }
398
399 fn update(
400 &self,
401 id: SourceId,
402 text: String,
403 range: Option<Selection>,
404 version: i32,
405 ) -> Result<(), SourceManagerError> {
406 let mut manager = self.0.write();
407 let source_file = &mut manager.files[id];
408 let source_file_cloned = Arc::make_mut(source_file);
409 source_file_cloned
410 .content_mut()
411 .update(text, range, version)
412 .map_err(SourceManagerError::InvalidContentUpdate)
413 }
414
415 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
416 let manager = self.0.read();
417 manager.get(id)
418 }
419
420 fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
421 let manager = self.0.read();
422 manager.get_by_uri(uri)
423 }
424
425 fn find(&self, uri: &Uri) -> Option<SourceId> {
426 let manager = self.0.read();
427 manager.find(uri)
428 }
429
430 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
431 let manager = self.0.read();
432 manager.file_line_col_to_span(loc)
433 }
434
435 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
436 let manager = self.0.read();
437 manager.file_line_col(span)
438 }
439
440 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
441 let manager = self.0.read();
442 manager.location_to_span(loc)
443 }
444
445 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
446 let manager = self.0.read();
447 manager.location(span)
448 }
449
450 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
451 let manager = self.0.read();
452 let ptr = manager
453 .files
454 .get(id)
455 .ok_or(SourceManagerError::InvalidSourceId)
456 .map(|file| file.as_str() as *const str)?;
457 drop(manager);
458 Ok(unsafe { &*ptr })
463 }
464
465 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
466 self.source(span.source_id())?
467 .get(span.into_slice_index())
468 .ok_or(SourceManagerError::InvalidBounds)
469 }
470}
471
472#[cfg(test)]
473mod error_assertions {
474 use super::*;
475
476 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
478
479 fn _assert_source_manager_error_bounds(err: SourceManagerError) {
480 _assert_error_is_send_sync_static(err);
481 }
482}