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