miden_debug_types/
source_manager.rs

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// SOURCE ID
9// ================================================================================================
10
11/// A [SourceId] represents the index/identifier associated with a unique source file in a
12/// [SourceManager] implementation.
13#[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    /// Create a new [SourceId] from a `u32` value, but assert if the value is reserved
42    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    /// Create a new [SourceId] from a raw `u32` value
49    #[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// SOURCE MANAGER
82// ================================================================================================
83
84/// The set of errors which may be raised by a [SourceManager]
85#[derive(Debug, thiserror::Error)]
86pub enum SourceManagerError {
87    /// A [SourceId] was provided to a [SourceManager] which was allocated by a different
88    /// [SourceManager]
89    #[error("attempted to use an invalid source id")]
90    InvalidSourceId,
91    /// An attempt was made to read content using invalid byte indices
92    #[error("attempted to read content out of bounds")]
93    InvalidBounds,
94    #[error(transparent)]
95    InvalidContentUpdate(#[from] SourceContentUpdateError),
96    /// Custom error variant for implementors of the trait.
97    #[error("{error_msg}")]
98    Custom {
99        error_msg: Box<str>,
100        // thiserror will return this when calling Error::source on SourceManagerError.
101        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    /// Returns true if `file` is managed by this source manager
120    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    /// Copies `file` into this source manager (if not already managed by this manager).
127    ///
128    /// The returned source file is guaranteed to be owned by this manager.
129    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    /// Load the given `content` into this [SourceManager] with `name`
138    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    /// Load content into this [SourceManager] from raw [SourceFile] components
143    fn load_from_raw_parts(&self, name: Uri, content: SourceContent) -> Arc<SourceFile>;
144    /// Update the source file corresponding to `id` after being notified of a change event.
145    ///
146    /// The `version` indicates the new version of the document
147    fn update(
148        &self,
149        id: SourceId,
150        text: String,
151        range: Option<Selection>,
152        version: i32,
153    ) -> Result<(), SourceManagerError>;
154    /// Get the [SourceFile] corresponding to `id`
155    fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError>;
156    /// Get the most recent [SourceFile] whose URI is `uri`
157    fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
158        self.find(uri).and_then(|id| self.get(id).ok())
159    }
160    /// Search for a source file whose URI is `uri`, and return its [SourceId] if found.
161    fn find(&self, uri: &Uri) -> Option<SourceId>;
162    /// Convert a [FileLineCol] to an equivalent [SourceSpan], if the referenced file is available
163    fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan>;
164    /// Convert a [SourceSpan] to an equivalent [FileLineCol], if the span is valid
165    fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError>;
166    /// Convert a [Location] to an equivalent [SourceSpan], if the referenced file is available
167    fn location_to_span(&self, loc: Location) -> Option<SourceSpan>;
168    /// Convert a [SourceSpan] to an equivalent [Location], if the span is valid
169    fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError>;
170    /// Get the source associated with `id` as a string slice
171    fn source(&self, id: SourceId) -> Result<&str, SourceManagerError>;
172    /// Get the source corresponding to `span` as a string slice
173    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    /// Load the content of `path` into this [SourceManager]
244    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
273/// [SourceManagerSync] is a marker trait for [SourceManager] implementations that are also Send +
274/// Sync, and is automatically implemented for any [SourceManager] that meets those requirements.
275///
276/// [SourceManager] is a supertrait of [SourceManagerSync], so you may use instances of the
277/// [SourceManagerSync] where the [SourceManager] is required, either implicitly or via explicit
278/// downcasting, e.g. `Arc<dyn SourceManagerSync> as Arc<dyn SourceManager>`.
279pub trait SourceManagerSync: SourceManager + Send + Sync {}
280
281impl<T: ?Sized + SourceManager + Send + Sync> SourceManagerSync for T {}
282
283// DEFAULT SOURCE MANAGER
284// ================================================================================================
285
286use 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 we have previously inserted the same content with `name`, return the previously
328        // inserted source id
329        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        // SAFETY: Because the lifetime of the returned reference is bound to the manager, and
459        // because we can only ever add files, not modify/remove them, this is safe. Exclusive
460        // access to the manager does _not_ mean exclusive access to the contents of previously
461        // added source files
462        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    /// Asserts at compile time that the passed error has Send + Sync + 'static bounds.
477    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}