miden_debug_types/
source_manager.rs

1use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::Arc, vec::Vec};
2use core::{error::Error, fmt::Debug};
3
4use super::*;
5
6// SOURCE ID
7// ================================================================================================
8
9/// A [SourceId] represents the index/identifier associated with a unique source file in a
10/// [SourceManager] implementation.
11#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
13#[cfg_attr(feature = "serde", serde(transparent))]
14pub struct SourceId(u32);
15
16impl Default for SourceId {
17    fn default() -> Self {
18        Self::UNKNOWN
19    }
20}
21
22impl SourceId {
23    pub const UNKNOWN: Self = Self(u32::MAX);
24
25    /// Create a new [SourceId] from a `u32` value, but assert if the value is reserved
26    pub fn new(id: u32) -> Self {
27        assert_ne!(id, u32::MAX, "u32::MAX is a reserved value for SourceId::default()/UNKNOWN");
28
29        Self(id)
30    }
31
32    /// Create a new [SourceId] from a raw `u32` value
33    #[inline(always)]
34    pub const fn new_unchecked(id: u32) -> Self {
35        Self(id)
36    }
37
38    #[inline(always)]
39    pub const fn to_usize(self) -> usize {
40        self.0 as usize
41    }
42
43    #[inline(always)]
44    pub const fn to_u32(self) -> u32 {
45        self.0
46    }
47
48    pub const fn is_unknown(&self) -> bool {
49        self.0 == u32::MAX
50    }
51}
52
53impl TryFrom<usize> for SourceId {
54    type Error = ();
55
56    #[inline]
57    fn try_from(id: usize) -> Result<Self, Self::Error> {
58        match u32::try_from(id) {
59            Ok(n) if n < u32::MAX => Ok(Self(n)),
60            _ => Err(()),
61        }
62    }
63}
64
65// SOURCE MANAGER
66// ================================================================================================
67
68/// The set of errors which may be raised by a [SourceManager]
69#[derive(Debug, thiserror::Error)]
70pub enum SourceManagerError {
71    /// A [SourceId] was provided to a [SourceManager] which was allocated by a different
72    /// [SourceManager]
73    #[error("attempted to use an invalid source id")]
74    InvalidSourceId,
75    /// An attempt was made to read content using invalid byte indices
76    #[error("attempted to read content out of bounds")]
77    InvalidBounds,
78    #[error(transparent)]
79    InvalidContentUpdate(#[from] SourceContentUpdateError),
80    /// Custom error variant for implementors of the trait.
81    #[error("{error_msg}")]
82    Custom {
83        error_msg: Box<str>,
84        // thiserror will return this when calling Error::source on SourceManagerError.
85        source: Option<Box<dyn Error + Send + Sync + 'static>>,
86    },
87}
88
89impl SourceManagerError {
90    pub fn custom(message: String) -> Self {
91        Self::Custom { error_msg: message.into(), source: None }
92    }
93
94    pub fn custom_with_source(message: String, source: impl Error + Send + Sync + 'static) -> Self {
95        Self::Custom {
96            error_msg: message.into(),
97            source: Some(Box::new(source)),
98        }
99    }
100}
101
102pub trait SourceManager: Debug {
103    /// Returns true if `file` is managed by this source manager
104    fn is_manager_of(&self, file: &SourceFile) -> bool {
105        match self.get(file.id()) {
106            Ok(found) => core::ptr::addr_eq(Arc::as_ptr(&found), file),
107            Err(_) => false,
108        }
109    }
110    /// Copies `file` into this source manager (if not already managed by this manager).
111    ///
112    /// The returned source file is guaranteed to be owned by this manager.
113    fn copy_into(&self, file: &SourceFile) -> Arc<SourceFile> {
114        if let Ok(found) = self.get(file.id()) {
115            if core::ptr::addr_eq(Arc::as_ptr(&found), file) {
116                return found;
117            }
118        }
119        self.load_from_raw_parts(file.uri().clone(), file.content().clone())
120    }
121    /// Load the given `content` into this [SourceManager] with `name`
122    fn load(&self, lang: SourceLanguage, name: Uri, content: String) -> Arc<SourceFile> {
123        let content = SourceContent::new(lang, name.clone(), content);
124        self.load_from_raw_parts(name, content)
125    }
126    /// Load content into this [SourceManager] from raw [SourceFile] components
127    fn load_from_raw_parts(&self, name: Uri, content: SourceContent) -> Arc<SourceFile>;
128    /// Update the source file corresponding to `id` after being notified of a change event.
129    ///
130    /// The `version` indicates the new version of the document
131    fn update(
132        &self,
133        id: SourceId,
134        text: String,
135        range: Option<Selection>,
136        version: i32,
137    ) -> Result<(), SourceManagerError>;
138    /// Get the [SourceFile] corresponding to `id`
139    fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError>;
140    /// Get the most recent [SourceFile] whose URI is `uri`
141    fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
142        self.find(uri).and_then(|id| self.get(id).ok())
143    }
144    /// Search for a source file whose URI is `uri`, and return its [SourceId] if found.
145    fn find(&self, uri: &Uri) -> Option<SourceId>;
146    /// Convert a [FileLineCol] to an equivalent [SourceSpan], if the referenced file is available
147    fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan>;
148    /// Convert a [SourceSpan] to an equivalent [FileLineCol], if the span is valid
149    fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError>;
150    /// Convert a [Location] to an equivalent [SourceSpan], if the referenced file is available
151    fn location_to_span(&self, loc: Location) -> Option<SourceSpan>;
152    /// Convert a [SourceSpan] to an equivalent [Location], if the span is valid
153    fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError>;
154    /// Get the source associated with `id` as a string slice
155    fn source(&self, id: SourceId) -> Result<&str, SourceManagerError>;
156    /// Get the source corresponding to `span` as a string slice
157    fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError>;
158}
159
160impl<T: ?Sized + SourceManager> SourceManager for Arc<T> {
161    #[inline(always)]
162    fn is_manager_of(&self, file: &SourceFile) -> bool {
163        (**self).is_manager_of(file)
164    }
165    #[inline(always)]
166    fn copy_into(&self, file: &SourceFile) -> Arc<SourceFile> {
167        (**self).copy_into(file)
168    }
169    #[inline(always)]
170    fn load(&self, lang: SourceLanguage, uri: Uri, content: String) -> Arc<SourceFile> {
171        (**self).load(lang, uri, content)
172    }
173    #[inline(always)]
174    fn load_from_raw_parts(&self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
175        (**self).load_from_raw_parts(uri, content)
176    }
177    #[inline(always)]
178    fn update(
179        &self,
180        id: SourceId,
181        text: String,
182        range: Option<Selection>,
183        version: i32,
184    ) -> Result<(), SourceManagerError> {
185        (**self).update(id, text, range, version)
186    }
187    #[inline(always)]
188    fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
189        (**self).get(id)
190    }
191    #[inline(always)]
192    fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
193        (**self).get_by_uri(uri)
194    }
195    #[inline(always)]
196    fn find(&self, uri: &Uri) -> Option<SourceId> {
197        (**self).find(uri)
198    }
199    #[inline(always)]
200    fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
201        (**self).file_line_col_to_span(loc)
202    }
203    #[inline(always)]
204    fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
205        (**self).file_line_col(span)
206    }
207    #[inline(always)]
208    fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
209        (**self).location_to_span(loc)
210    }
211    #[inline(always)]
212    fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
213        (**self).location(span)
214    }
215    #[inline(always)]
216    fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
217        (**self).source(id)
218    }
219    #[inline(always)]
220    fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
221        (**self).source_slice(span)
222    }
223}
224
225#[cfg(feature = "std")]
226pub trait SourceManagerExt: SourceManager {
227    /// Load the content of `path` into this [SourceManager]
228    fn load_file(&self, path: &std::path::Path) -> Result<Arc<SourceFile>, SourceManagerError> {
229        let uri = Uri::from(path);
230        if let Some(existing) = self.get_by_uri(&uri) {
231            return Ok(existing);
232        }
233
234        let lang = match path.extension().and_then(|ext| ext.to_str()) {
235            Some("masm") => "masm",
236            Some("rs") => "rust",
237            Some(ext) => ext,
238            None => "unknown",
239        };
240
241        let content = std::fs::read_to_string(path)
242            .map(|s| SourceContent::new(lang, uri.clone(), s))
243            .map_err(|source| {
244                SourceManagerError::custom_with_source(
245                    format!("failed to load filed at `{}`", path.display()),
246                    source,
247                )
248            })?;
249
250        Ok(self.load_from_raw_parts(uri, content))
251    }
252}
253
254#[cfg(feature = "std")]
255impl<T: ?Sized + SourceManager> SourceManagerExt for T {}
256
257// DEFAULT SOURCE MANAGER
258// ================================================================================================
259
260use miden_utils_sync::RwLock;
261
262#[derive(Debug, Default)]
263pub struct DefaultSourceManager(RwLock<DefaultSourceManagerImpl>);
264impl Clone for DefaultSourceManager {
265    fn clone(&self) -> Self {
266        let manager = self.0.read();
267        Self(RwLock::new(manager.clone()))
268    }
269}
270
271#[derive(Debug, Default, Clone)]
272struct DefaultSourceManagerImpl {
273    files: Vec<Arc<SourceFile>>,
274    uris: BTreeMap<Uri, SourceId>,
275}
276
277impl DefaultSourceManagerImpl {
278    fn insert(&mut self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
279        // If we have previously inserted the same content with `name`, return the previously
280        // inserted source id
281        if let Some(file) = self.uris.get(&uri).copied().and_then(|id| {
282            let file = &self.files[id.to_usize()];
283            if file.as_str() == content.as_str() {
284                Some(Arc::clone(file))
285            } else {
286                None
287            }
288        }) {
289            return file;
290        }
291        let id = SourceId::try_from(self.files.len())
292            .expect("system limit: source manager has exhausted its supply of source ids");
293        let file = Arc::new(SourceFile::from_raw_parts(id, content));
294        self.files.push(Arc::clone(&file));
295        self.uris.insert(uri.clone(), id);
296        file
297    }
298
299    fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
300        self.files
301            .get(id.to_usize())
302            .cloned()
303            .ok_or(SourceManagerError::InvalidSourceId)
304    }
305
306    fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
307        self.find(uri).and_then(|id| self.get(id).ok())
308    }
309
310    fn find(&self, uri: &Uri) -> Option<SourceId> {
311        self.uris.get(uri).copied()
312    }
313
314    fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
315        let file = self.uris.get(&loc.uri).copied().and_then(|id| self.files.get(id.to_usize()))?;
316        file.line_column_to_span(loc.line, loc.column)
317    }
318
319    fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
320        self.files
321            .get(span.source_id().to_usize())
322            .ok_or(SourceManagerError::InvalidSourceId)
323            .map(|file| file.location(span))
324    }
325
326    fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
327        let file = self.uris.get(&loc.uri).copied().and_then(|id| self.files.get(id.to_usize()))?;
328
329        let max_len = ByteIndex::from(file.as_str().len() as u32);
330        if loc.start >= max_len || loc.end > max_len {
331            return None;
332        }
333
334        Some(SourceSpan::new(file.id(), loc.start..loc.end))
335    }
336
337    fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
338        self.files
339            .get(span.source_id().to_usize())
340            .ok_or(SourceManagerError::InvalidSourceId)
341            .map(|file| Location::new(file.uri().clone(), span.start(), span.end()))
342    }
343}
344
345impl SourceManager for DefaultSourceManager {
346    fn load_from_raw_parts(&self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
347        let mut manager = self.0.write();
348        manager.insert(uri, content)
349    }
350
351    fn update(
352        &self,
353        id: SourceId,
354        text: String,
355        range: Option<Selection>,
356        version: i32,
357    ) -> Result<(), SourceManagerError> {
358        let mut manager = self.0.write();
359        let source_file = manager
360            .files
361            .get_mut(id.to_usize())
362            .ok_or(SourceManagerError::InvalidSourceId)?;
363        let source_file = Arc::make_mut(source_file);
364        source_file
365            .content_mut()
366            .update(text, range, version)
367            .map_err(SourceManagerError::InvalidContentUpdate)
368    }
369
370    fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
371        let manager = self.0.read();
372        manager.get(id)
373    }
374
375    fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
376        let manager = self.0.read();
377        manager.get_by_uri(uri)
378    }
379
380    fn find(&self, uri: &Uri) -> Option<SourceId> {
381        let manager = self.0.read();
382        manager.find(uri)
383    }
384
385    fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
386        let manager = self.0.read();
387        manager.file_line_col_to_span(loc)
388    }
389
390    fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
391        let manager = self.0.read();
392        manager.file_line_col(span)
393    }
394
395    fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
396        let manager = self.0.read();
397        manager.location_to_span(loc)
398    }
399
400    fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
401        let manager = self.0.read();
402        manager.location(span)
403    }
404
405    fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
406        let manager = self.0.read();
407        let ptr = manager
408            .files
409            .get(id.to_usize())
410            .ok_or(SourceManagerError::InvalidSourceId)
411            .map(|file| file.as_str() as *const str)?;
412        drop(manager);
413        // SAFETY: Because the lifetime of the returned reference is bound to the manager, and
414        // because we can only ever add files, not modify/remove them, this is safe. Exclusive
415        // access to the manager does _not_ mean exclusive access to the contents of previously
416        // added source files
417        Ok(unsafe { &*ptr })
418    }
419
420    fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
421        self.source(span.source_id())?
422            .get(span.into_slice_index())
423            .ok_or(SourceManagerError::InvalidBounds)
424    }
425}
426
427#[cfg(test)]
428mod error_assertions {
429    use super::*;
430
431    /// Asserts at compile time that the passed error has Send + Sync + 'static bounds.
432    fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
433
434    fn _assert_source_manager_error_bounds(err: SourceManagerError) {
435        _assert_error_is_send_sync_static(err);
436    }
437}