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(
19 all(feature = "arbitrary", test),
20 miden_test_serialization_macros::serialization_test
21)]
22pub struct SourceId(u32);
23
24impl Serializable for SourceId {
25 fn write_into<W: ByteWriter>(&self, target: &mut W) {
26 self.0.write_into(target);
27 }
28}
29
30impl Deserializable for SourceId {
31 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
32 u32::read_from(source).map(Self)
33 }
34}
35
36impl From<u32> for SourceId {
37 fn from(value: u32) -> Self {
38 SourceId::new_unchecked(value)
39 }
40}
41
42impl From<SourceId> for u32 {
43 fn from(value: SourceId) -> Self {
44 value.to_u32()
45 }
46}
47
48impl miden_utils_indexing::Idx for SourceId {}
49
50impl Default for SourceId {
51 fn default() -> Self {
52 Self::UNKNOWN
53 }
54}
55
56impl SourceId {
57 pub const UNKNOWN: Self = Self(u32::MAX);
58
59 pub fn new(id: u32) -> Self {
61 assert_ne!(id, u32::MAX, "u32::MAX is a reserved value for SourceId::default()/UNKNOWN");
62
63 Self(id)
64 }
65
66 #[inline(always)]
68 pub const fn new_unchecked(id: u32) -> Self {
69 Self(id)
70 }
71
72 #[inline(always)]
73 pub const fn to_usize(self) -> usize {
74 self.0 as usize
75 }
76
77 #[inline(always)]
78 pub const fn to_u32(self) -> u32 {
79 self.0
80 }
81
82 pub const fn is_unknown(&self) -> bool {
83 self.0 == u32::MAX
84 }
85}
86
87impl TryFrom<usize> for SourceId {
88 type Error = ();
89
90 #[inline]
91 fn try_from(id: usize) -> Result<Self, Self::Error> {
92 match u32::try_from(id) {
93 Ok(n) if n < u32::MAX => Ok(Self(n)),
94 _ => Err(()),
95 }
96 }
97}
98
99#[cfg(feature = "arbitrary")]
100impl Arbitrary for SourceId {
101 type Parameters = ();
102 type Strategy = BoxedStrategy<Self>;
103
104 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
105 any::<u32>().prop_map(Self::from).boxed()
106 }
107}
108
109#[derive(Debug, thiserror::Error)]
114pub enum SourceManagerError {
115 #[error("attempted to use an invalid source id")]
118 InvalidSourceId,
119 #[error("attempted to read content out of bounds")]
121 InvalidBounds,
122 #[error(transparent)]
123 InvalidContentUpdate(#[from] SourceContentUpdateError),
124 #[error("{error_msg}")]
126 Custom {
127 error_msg: Box<str>,
128 source: Option<Box<dyn Error + Send + Sync + 'static>>,
130 },
131}
132
133impl SourceManagerError {
134 pub fn custom(message: String) -> Self {
135 Self::Custom { error_msg: message.into(), source: None }
136 }
137
138 pub fn custom_with_source(message: String, source: impl Error + Send + Sync + 'static) -> Self {
139 Self::Custom {
140 error_msg: message.into(),
141 source: Some(Box::new(source)),
142 }
143 }
144}
145
146pub trait SourceManager: Debug {
147 fn is_manager_of(&self, file: &SourceFile) -> bool {
149 match self.get(file.id()) {
150 Ok(found) => core::ptr::addr_eq(Arc::as_ptr(&found), file),
151 Err(_) => false,
152 }
153 }
154 fn copy_into(&self, file: &SourceFile) -> Arc<SourceFile> {
158 if let Ok(found) = self.get(file.id())
159 && core::ptr::addr_eq(Arc::as_ptr(&found), file)
160 {
161 return found;
162 }
163 self.load_from_raw_parts(file.uri().clone(), file.content().clone())
164 }
165 fn load(&self, lang: SourceLanguage, name: Uri, content: String) -> Arc<SourceFile> {
167 let content = SourceContent::new(lang, name.clone(), content);
168 self.load_from_raw_parts(name, content)
169 }
170 fn load_anonymous(&self, lang: SourceLanguage, content: String) -> Arc<SourceFile> {
173 use alloc::format;
174
175 use miden_crypto::hash::sha2::Sha256;
176 let digest = Sha256::hash(content.as_bytes());
177 let name = Uri::new(format!("memory://{}", String::from(digest)));
178 let content = SourceContent::new(lang, name.clone(), content);
179 self.load_from_raw_parts(name, content)
180 }
181 fn load_from_raw_parts(&self, name: Uri, content: SourceContent) -> Arc<SourceFile>;
183 fn update(
187 &self,
188 id: SourceId,
189 text: String,
190 range: Option<Selection>,
191 version: i32,
192 ) -> Result<(), SourceManagerError>;
193 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError>;
195 fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
197 self.find(uri).and_then(|id| self.get(id).ok())
198 }
199 fn find(&self, uri: &Uri) -> Option<SourceId>;
201 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan>;
203 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError>;
205 fn location_to_span(&self, loc: Location) -> Option<SourceSpan>;
207 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError>;
209 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError>;
211 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError>;
213}
214
215impl<T: ?Sized + SourceManager> SourceManager for Arc<T> {
216 #[inline(always)]
217 fn is_manager_of(&self, file: &SourceFile) -> bool {
218 (**self).is_manager_of(file)
219 }
220 #[inline(always)]
221 fn copy_into(&self, file: &SourceFile) -> Arc<SourceFile> {
222 (**self).copy_into(file)
223 }
224 #[inline(always)]
225 fn load(&self, lang: SourceLanguage, uri: Uri, content: String) -> Arc<SourceFile> {
226 (**self).load(lang, uri, content)
227 }
228 #[inline(always)]
229 fn load_from_raw_parts(&self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
230 (**self).load_from_raw_parts(uri, content)
231 }
232 #[inline(always)]
233 fn update(
234 &self,
235 id: SourceId,
236 text: String,
237 range: Option<Selection>,
238 version: i32,
239 ) -> Result<(), SourceManagerError> {
240 (**self).update(id, text, range, version)
241 }
242 #[inline(always)]
243 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
244 (**self).get(id)
245 }
246 #[inline(always)]
247 fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
248 (**self).get_by_uri(uri)
249 }
250 #[inline(always)]
251 fn find(&self, uri: &Uri) -> Option<SourceId> {
252 (**self).find(uri)
253 }
254 #[inline(always)]
255 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
256 (**self).file_line_col_to_span(loc)
257 }
258 #[inline(always)]
259 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
260 (**self).file_line_col(span)
261 }
262 #[inline(always)]
263 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
264 (**self).location_to_span(loc)
265 }
266 #[inline(always)]
267 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
268 (**self).location(span)
269 }
270 #[inline(always)]
271 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
272 (**self).source(id)
273 }
274 #[inline(always)]
275 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
276 (**self).source_slice(span)
277 }
278}
279
280#[cfg(feature = "std")]
281pub trait SourceManagerExt: SourceManager {
282 fn load_file(&self, path: &std::path::Path) -> Result<Arc<SourceFile>, SourceManagerError> {
284 let uri = Uri::from(path);
285 let content = std::fs::read_to_string(path).map_err(|source| {
286 SourceManagerError::custom_with_source(
287 alloc::format!("failed to load file at `{}`", path.display()),
288 source,
289 )
290 })?;
291
292 if let Some(existing) = self.get_by_uri(&uri)
294 && existing.as_str() == content.as_str()
295 {
296 return Ok(existing);
297 }
298
299 let lang = match path.extension().and_then(|ext| ext.to_str()) {
300 Some("masm") => "masm",
301 Some("rs") => "rust",
302 Some(ext) => ext,
303 None => "unknown",
304 };
305
306 let content = std::fs::read_to_string(path)
307 .map(|s| SourceContent::new(lang, uri.clone(), s))
308 .map_err(|source| {
309 SourceManagerError::custom_with_source(
310 alloc::format!("failed to load file at `{}`", path.display()),
311 source,
312 )
313 })?;
314
315 Ok(self.load_from_raw_parts(uri, content))
316 }
317}
318
319#[cfg(feature = "std")]
320impl<T: ?Sized + SourceManager> SourceManagerExt for T {}
321
322pub trait SourceManagerSync: SourceManager + Send + Sync {}
329
330impl<T: ?Sized + SourceManager + Send + Sync> SourceManagerSync for T {}
331
332use miden_utils_sync::RwLock;
336
337#[derive(Debug, Default)]
338pub struct DefaultSourceManager(RwLock<DefaultSourceManagerImpl>);
339
340impl Default for DefaultSourceManagerImpl {
341 fn default() -> Self {
342 Self::new()
343 }
344}
345impl Clone for DefaultSourceManager {
346 fn clone(&self) -> Self {
347 let manager = self.0.read();
348 Self(RwLock::new(manager.clone()))
349 }
350}
351
352impl Clone for DefaultSourceManagerImpl {
353 fn clone(&self) -> Self {
354 Self {
355 files: self.files.clone(),
356 uris: self.uris.clone(),
357 }
358 }
359}
360
361#[derive(Debug)]
362struct DefaultSourceManagerImpl {
363 files: IndexVec<SourceId, Arc<SourceFile>>,
364 uris: BTreeMap<Uri, SourceId>,
365}
366
367impl DefaultSourceManagerImpl {
368 fn new() -> Self {
369 Self {
370 files: IndexVec::new(),
371 uris: BTreeMap::new(),
372 }
373 }
374
375 fn insert(&mut self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
376 if let Some(file) = self.uris.get(&uri).copied().and_then(|id| {
379 let file = &self.files[id];
380 if file.as_str() == content.as_str() {
381 Some(Arc::clone(file))
382 } else {
383 None
384 }
385 }) {
386 return file;
387 }
388 let id = SourceId::try_from(self.files.len())
389 .expect("system limit: source manager has exhausted its supply of source ids");
390 let file = Arc::new(SourceFile::from_raw_parts(id, content));
391 let file_clone = Arc::clone(&file);
392 self.files
393 .push(file_clone)
394 .expect("system limit: source manager has exhausted its supply of source ids");
395 self.uris.insert(uri, id);
396 file
397 }
398
399 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
400 self.files.get(id).cloned().ok_or(SourceManagerError::InvalidSourceId)
401 }
402
403 fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
404 self.find(uri).and_then(|id| self.get(id).ok())
405 }
406
407 fn find(&self, uri: &Uri) -> Option<SourceId> {
408 self.uris.get(uri).copied()
409 }
410
411 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
412 let file = self.uris.get(&loc.uri).copied().and_then(|id| self.files.get(id))?;
413 file.line_column_to_span(loc.line, loc.column)
414 }
415
416 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
417 self.files
418 .get(span.source_id())
419 .ok_or(SourceManagerError::InvalidSourceId)
420 .map(|file| file.location(span))
421 }
422
423 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
424 let file = self.uris.get(&loc.uri).copied().and_then(|id| self.files.get(id))?;
425
426 let max_len = ByteIndex::from(file.as_str().len() as u32);
427 if loc.start >= max_len || loc.end > max_len {
428 return None;
429 }
430
431 Some(SourceSpan::new(file.id(), loc.start..loc.end))
432 }
433
434 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
435 self.files
436 .get(span.source_id())
437 .ok_or(SourceManagerError::InvalidSourceId)
438 .map(|file| Location::new(file.uri().clone(), span.start(), span.end()))
439 }
440}
441
442impl SourceManager for DefaultSourceManager {
443 fn load_from_raw_parts(&self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
444 let mut manager = self.0.write();
445 manager.insert(uri, content)
446 }
447
448 fn update(
449 &self,
450 id: SourceId,
451 text: String,
452 range: Option<Selection>,
453 version: i32,
454 ) -> Result<(), SourceManagerError> {
455 let mut manager = self.0.write();
456 let source_file = &mut manager.files[id];
457 let source_file_cloned = Arc::make_mut(source_file);
458 source_file_cloned
459 .content_mut()
460 .update(text, range, version)
461 .map_err(SourceManagerError::InvalidContentUpdate)
462 }
463
464 fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
465 let manager = self.0.read();
466 manager.get(id)
467 }
468
469 fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
470 let manager = self.0.read();
471 manager.get_by_uri(uri)
472 }
473
474 fn find(&self, uri: &Uri) -> Option<SourceId> {
475 let manager = self.0.read();
476 manager.find(uri)
477 }
478
479 fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
480 let manager = self.0.read();
481 manager.file_line_col_to_span(loc)
482 }
483
484 fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
485 let manager = self.0.read();
486 manager.file_line_col(span)
487 }
488
489 fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
490 let manager = self.0.read();
491 manager.location_to_span(loc)
492 }
493
494 fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
495 let manager = self.0.read();
496 manager.location(span)
497 }
498
499 fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
500 let manager = self.0.read();
501 let ptr = manager
502 .files
503 .get(id)
504 .ok_or(SourceManagerError::InvalidSourceId)
505 .map(|file| file.as_str() as *const str)?;
506 drop(manager);
507 Ok(unsafe { &*ptr })
512 }
513
514 fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
515 self.source(span.source_id())?
516 .get(span.into_slice_index())
517 .ok_or(SourceManagerError::InvalidBounds)
518 }
519}
520
521#[cfg(test)]
522mod error_assertions {
523 use super::*;
524
525 fn _assert_error_is_send_sync_static<E: Error + Send + Sync + 'static>(_: E) {}
527
528 fn _assert_source_manager_error_bounds(err: SourceManagerError) {
529 _assert_error_is_send_sync_static(err);
530 }
531}