joule_common/
source_map.rs

1//! Source file mapping and tracking
2//!
3//! This module provides utilities for tracking source files and their contents
4//! for error reporting and debugging.
5
6use std::collections::HashMap;
7use std::sync::atomic::{AtomicU32, Ordering};
8
9/// Unique identifier for a source file
10pub type FileId = u32;
11
12/// Information about a source file
13#[derive(Debug, Clone)]
14pub struct FileInfo {
15    /// File ID
16    pub id: FileId,
17    /// File name or path
18    pub name: String,
19    /// Source code content
20    pub source: String,
21}
22
23/// Maps file IDs to source files
24#[derive(Debug)]
25pub struct SourceMap {
26    files: HashMap<FileId, FileInfo>,
27    next_id: AtomicU32,
28}
29
30impl SourceMap {
31    /// Create a new empty source map
32    pub fn new() -> Self {
33        Self {
34            files: HashMap::new(),
35            next_id: AtomicU32::new(1), // Start at 1, reserve 0 for special cases
36        }
37    }
38
39    /// Add a source file and return its ID
40    pub fn add_file(&mut self, name: String, source: String) -> FileId {
41        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
42        let info = FileInfo { id, name, source };
43        self.files.insert(id, info);
44        id
45    }
46
47    /// Get information about a file
48    pub fn get_file(&self, id: FileId) -> Option<&FileInfo> {
49        self.files.get(&id)
50    }
51
52    /// Get all files
53    pub fn files(&self) -> impl Iterator<Item = (&FileId, &FileInfo)> {
54        self.files.iter()
55    }
56
57    /// Get the number of files
58    pub fn len(&self) -> usize {
59        self.files.len()
60    }
61
62    /// Check if the source map is empty
63    pub fn is_empty(&self) -> bool {
64        self.files.is_empty()
65    }
66}
67
68impl Default for SourceMap {
69    fn default() -> Self {
70        Self::new()
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_source_map_basic() {
80        let mut map = SourceMap::new();
81        let id = map.add_file("test.joule".to_string(), "fn main() {}".to_string());
82
83        assert_eq!(map.len(), 1);
84        let info = map.get_file(id).unwrap();
85        assert_eq!(info.name, "test.joule");
86        assert_eq!(info.source, "fn main() {}");
87    }
88
89    #[test]
90    fn test_multiple_files() {
91        let mut map = SourceMap::new();
92        let id1 = map.add_file("file1.joule".to_string(), "source1".to_string());
93        let id2 = map.add_file("file2.joule".to_string(), "source2".to_string());
94
95        assert_eq!(map.len(), 2);
96        assert_ne!(id1, id2);
97        assert!(map.get_file(id1).is_some());
98        assert!(map.get_file(id2).is_some());
99    }
100}