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_anonymous(&self, lang: SourceLanguage, content: String) -> Arc<SourceFile> {
158 use alloc::format;
159
160 use miden_crypto::hash::sha2::Sha256;
161 let digest = Sha256::hash(content.as_bytes());
162 let name = Uri::new(format!("memory://{}", String::from(digest)));
163 let content = SourceContent::new(lang, name.clone(), content);
164 self.load_from_raw_parts(name, content)
165 }
166 fn load_from_raw_parts(&self, name: Uri, content: SourceContent) -> Arc<SourceFile>;
168 fn update(
172 &self,
173 id: SourceId,
174 text: String,
175 range: Option<Selection>,
176 version: i32,
177 ) -> Result<(), SourceManagerError>;
178 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError>;
180 fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
182 self.find(uri).and_then(|id| self.get(id).ok())
183 }
184 fn find(&self, uri: &Uri) -> Option<SourceId>;
186 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan>;
188 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError>;
190 fn location_to_span(&self, loc: Location) -> Option<SourceSpan>;
192 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError>;
194 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError>;
196 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError>;
198}
199
200impl<T: ?Sized + SourceManager> SourceManager for Arc<T> {
201 #[inline(always)]
202 fn is_manager_of(&self, file: &SourceFile) -> bool {
203 (**self).is_manager_of(file)
204 }
205 #[inline(always)]
206 fn copy_into(&self, file: &SourceFile) -> Arc<SourceFile> {
207 (**self).copy_into(file)
208 }
209 #[inline(always)]
210 fn load(&self, lang: SourceLanguage, uri: Uri, content: String) -> Arc<SourceFile> {
211 (**self).load(lang, uri, content)
212 }
213 #[inline(always)]
214 fn load_from_raw_parts(&self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
215 (**self).load_from_raw_parts(uri, content)
216 }
217 #[inline(always)]
218 fn update(
219 &self,
220 id: SourceId,
221 text: String,
222 range: Option<Selection>,
223 version: i32,
224 ) -> Result<(), SourceManagerError> {
225 (**self).update(id, text, range, version)
226 }
227 #[inline(always)]
228 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
229 (**self).get(id)
230 }
231 #[inline(always)]
232 fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
233 (**self).get_by_uri(uri)
234 }
235 #[inline(always)]
236 fn find(&self, uri: &Uri) -> Option<SourceId> {
237 (**self).find(uri)
238 }
239 #[inline(always)]
240 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
241 (**self).file_line_col_to_span(loc)
242 }
243 #[inline(always)]
244 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
245 (**self).file_line_col(span)
246 }
247 #[inline(always)]
248 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
249 (**self).location_to_span(loc)
250 }
251 #[inline(always)]
252 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
253 (**self).location(span)
254 }
255 #[inline(always)]
256 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
257 (**self).source(id)
258 }
259 #[inline(always)]
260 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
261 (**self).source_slice(span)
262 }
263}
264
265#[cfg(feature = "std")]
266pub trait SourceManagerExt: SourceManager {
267 fn load_file(&self, path: &std::path::Path) -> Result<Arc<SourceFile>, SourceManagerError> {
269 let uri = Uri::from(path);
270 let content = std::fs::read_to_string(path).map_err(|source| {
271 SourceManagerError::custom_with_source(
272 alloc::format!("failed to load file at `{}`", path.display()),
273 source,
274 )
275 })?;
276
277 if let Some(existing) = self.get_by_uri(&uri)
279 && existing.as_str() == content.as_str()
280 {
281 return Ok(existing);
282 }
283
284 let lang = match path.extension().and_then(|ext| ext.to_str()) {
285 Some("masm") => "masm",
286 Some("rs") => "rust",
287 Some(ext) => ext,
288 None => "unknown",
289 };
290
291 let content = std::fs::read_to_string(path)
292 .map(|s| SourceContent::new(lang, uri.clone(), s))
293 .map_err(|source| {
294 SourceManagerError::custom_with_source(
295 alloc::format!("failed to load file at `{}`", path.display()),
296 source,
297 )
298 })?;
299
300 Ok(self.load_from_raw_parts(uri, content))
301 }
302}
303
304#[cfg(feature = "std")]
305impl<T: ?Sized + SourceManager> SourceManagerExt for T {}
306
307pub trait SourceManagerSync: SourceManager + Send + Sync {}
314
315impl<T: ?Sized + SourceManager + Send + Sync> SourceManagerSync for T {}
316
317use miden_utils_sync::RwLock;
321
322#[derive(Debug, Default)]
323pub struct DefaultSourceManager(RwLock<DefaultSourceManagerImpl>);
324
325impl Default for DefaultSourceManagerImpl {
326 fn default() -> Self {
327 Self::new()
328 }
329}
330impl Clone for DefaultSourceManager {
331 fn clone(&self) -> Self {
332 let manager = self.0.read();
333 Self(RwLock::new(manager.clone()))
334 }
335}
336
337impl Clone for DefaultSourceManagerImpl {
338 fn clone(&self) -> Self {
339 Self {
340 files: self.files.clone(),
341 uris: self.uris.clone(),
342 }
343 }
344}
345
346#[derive(Debug)]
347struct DefaultSourceManagerImpl {
348 files: IndexVec<SourceId, Arc<SourceFile>>,
349 uris: BTreeMap<Uri, SourceId>,
350}
351
352impl DefaultSourceManagerImpl {
353 fn new() -> Self {
354 Self {
355 files: IndexVec::new(),
356 uris: BTreeMap::new(),
357 }
358 }
359
360 fn insert(&mut self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
361 if let Some(file) = self.uris.get(&uri).copied().and_then(|id| {
364 let file = &self.files[id];
365 if file.as_str() == content.as_str() {
366 Some(Arc::clone(file))
367 } else {
368 None
369 }
370 }) {
371 return file;
372 }
373 let id = SourceId::try_from(self.files.len())
374 .expect("system limit: source manager has exhausted its supply of source ids");
375 let file = Arc::new(SourceFile::from_raw_parts(id, content));
376 let file_clone = Arc::clone(&file);
377 self.files
378 .push(file_clone)
379 .expect("system limit: source manager has exhausted its supply of source ids");
380 self.uris.insert(uri, id);
381 file
382 }
383
384 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
385 self.files.get(id).cloned().ok_or(SourceManagerError::InvalidSourceId)
386 }
387
388 fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
389 self.find(uri).and_then(|id| self.get(id).ok())
390 }
391
392 fn find(&self, uri: &Uri) -> Option<SourceId> {
393 self.uris.get(uri).copied()
394 }
395
396 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
397 let file = self.uris.get(&loc.uri).copied().and_then(|id| self.files.get(id))?;
398 file.line_column_to_span(loc.line, loc.column)
399 }
400
401 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
402 self.files
403 .get(span.source_id())
404 .ok_or(SourceManagerError::InvalidSourceId)
405 .map(|file| file.location(span))
406 }
407
408 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
409 let file = self.uris.get(&loc.uri).copied().and_then(|id| self.files.get(id))?;
410
411 let max_len = ByteIndex::from(file.as_str().len() as u32);
412 if loc.start >= max_len || loc.end > max_len {
413 return None;
414 }
415
416 Some(SourceSpan::new(file.id(), loc.start..loc.end))
417 }
418
419 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
420 self.files
421 .get(span.source_id())
422 .ok_or(SourceManagerError::InvalidSourceId)
423 .map(|file| Location::new(file.uri().clone(), span.start(), span.end()))
424 }
425}
426
427impl SourceManager for DefaultSourceManager {
428 fn load_from_raw_parts(&self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
429 let mut manager = self.0.write();
430 manager.insert(uri, content)
431 }
432
433 fn update(
434 &self,
435 id: SourceId,
436 text: String,
437 range: Option<Selection>,
438 version: i32,
439 ) -> Result<(), SourceManagerError> {
440 let mut manager = self.0.write();
441 let source_file = &mut manager.files[id];
442 let source_file_cloned = Arc::make_mut(source_file);
443 source_file_cloned
444 .content_mut()
445 .update(text, range, version)
446 .map_err(SourceManagerError::InvalidContentUpdate)
447 }
448
449 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
450 let manager = self.0.read();
451 manager.get(id)
452 }
453
454 fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
455 let manager = self.0.read();
456 manager.get_by_uri(uri)
457 }
458
459 fn find(&self, uri: &Uri) -> Option<SourceId> {
460 let manager = self.0.read();
461 manager.find(uri)
462 }
463
464 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
465 let manager = self.0.read();
466 manager.file_line_col_to_span(loc)
467 }
468
469 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
470 let manager = self.0.read();
471 manager.file_line_col(span)
472 }
473
474 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
475 let manager = self.0.read();
476 manager.location_to_span(loc)
477 }
478
479 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
480 let manager = self.0.read();
481 manager.location(span)
482 }
483
484 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
485 let manager = self.0.read();
486 let ptr = manager
487 .files
488 .get(id)
489 .ok_or(SourceManagerError::InvalidSourceId)
490 .map(|file| file.as_str() as *const str)?;
491 drop(manager);
492 Ok(unsafe { &*ptr })
497 }
498
499 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
500 self.source(span.source_id())?
501 .get(span.into_slice_index())
502 .ok_or(SourceManagerError::InvalidBounds)
503 }
504}
505
506#[cfg(test)]
507mod error_assertions {
508 use super::*;
509
510 fn _assert_error_is_send_sync_static<E: Error + Send + Sync + 'static>(_: E) {}
512
513 fn _assert_source_manager_error_bounds(err: SourceManagerError) {
514 _assert_error_is_send_sync_static(err);
515 }
516}