amarok_syntax/
source_map.rs

1use std::path::{Path, PathBuf};
2
3/// Identifies a source file registered with a [`SourceMap`].
4///
5/// `FileId::DUMMY` is used for spans not tied to any file (e.g. synthetic
6/// spans from `Span::zero()` and tests that don't go through a `SourceMap`).
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub struct FileId(pub u32);
9
10impl FileId {
11    pub const DUMMY: FileId = FileId(u32::MAX);
12
13    #[must_use]
14    pub fn is_dummy(self) -> bool {
15        self == FileId::DUMMY
16    }
17}
18
19/// A single source file registered with a [`SourceMap`].
20#[derive(Debug, Clone)]
21pub struct SourceFile {
22    pub path: PathBuf,
23    pub source: String,
24}
25
26/// Registry of source files participating in a single compilation/run.
27///
28/// Diagnostics carry a `FileId` (via their `Span`); the renderer looks up the
29/// file's path and source via this map.
30#[derive(Debug, Default, Clone)]
31pub struct SourceMap {
32    files: Vec<SourceFile>,
33}
34
35impl SourceMap {
36    #[must_use]
37    pub fn new() -> Self {
38        Self { files: Vec::new() }
39    }
40
41    /// Register a file and return its [`FileId`].
42    ///
43    /// # Panics
44    ///
45    /// Panics if the number of registered files exceeds `u32::MAX`.
46    pub fn add_file(&mut self, path: impl Into<PathBuf>, source: impl Into<String>) -> FileId {
47        let id = FileId(u32::try_from(self.files.len()).expect("too many source files"));
48        self.files.push(SourceFile {
49            path: path.into(),
50            source: source.into(),
51        });
52        id
53    }
54
55    #[must_use]
56    pub fn get(&self, file_id: FileId) -> Option<&SourceFile> {
57        if file_id.is_dummy() {
58            return None;
59        }
60        self.files.get(file_id.0 as usize)
61    }
62
63    #[must_use]
64    pub fn path_of(&self, file_id: FileId) -> Option<&Path> {
65        self.get(file_id).map(|f| f.path.as_path())
66    }
67
68    #[must_use]
69    pub fn source_of(&self, file_id: FileId) -> Option<&str> {
70        self.get(file_id).map(|f| f.source.as_str())
71    }
72}