nova_diagnostics/
files.rs1use std::collections::HashMap;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
7pub struct FileId(u32);
8
9impl FileId {
10 pub const DUMMY: Self = Self(u32::MAX);
12}
13
14#[derive(Debug, Default)]
16pub struct FileDb {
17 files: Vec<SourceFile>,
18 by_name: HashMap<String, FileId>,
19}
20
21#[derive(Debug)]
22struct SourceFile {
23 name: String,
24 source: String,
25 line_starts: Vec<u32>,
27}
28
29impl SourceFile {
30 fn new(name: String, source: String) -> Self {
31 let line_starts = std::iter::once(0)
32 .chain(source.match_indices('\n').map(|(i, _)| (i + 1) as u32))
33 .collect();
34 Self {
35 name,
36 source,
37 line_starts,
38 }
39 }
40}
41
42impl FileDb {
43 pub fn new() -> Self {
44 Self::default()
45 }
46
47 pub fn add(&mut self, name: impl Into<String>, source: impl Into<String>) -> FileId {
49 let name = name.into();
50 let source = source.into();
51 let id = FileId(self.files.len() as u32);
52 self.by_name.insert(name.clone(), id);
53 self.files.push(SourceFile::new(name, source));
54 id
55 }
56
57 pub fn get_source(&self, id: FileId) -> Option<&str> {
58 self.files.get(id.0 as usize).map(|f| f.source.as_str())
59 }
60
61 pub fn get_name(&self, id: FileId) -> Option<&str> {
62 self.files.get(id.0 as usize).map(|f| f.name.as_str())
63 }
64
65 pub fn id_of(&self, name: &str) -> Option<FileId> {
67 self.by_name.get(name).copied()
68 }
69
70 pub fn location(&self, id: FileId, offset: u32) -> Option<(usize, usize)> {
72 let file = self.files.get(id.0 as usize)?;
73 let line_idx = file
74 .line_starts
75 .partition_point(|&start| start <= offset)
76 .saturating_sub(1);
77 let line_start = file.line_starts[line_idx] as usize;
78 let col = offset as usize - line_start + 1;
79 Some((line_idx + 1, col))
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn a_file_is_found_by_its_name() {
89 let mut db = FileDb::new();
90 let a = db.add("<std/core>", "a");
91 let b = db.add("b.nova", "b");
92 assert_eq!(db.id_of("<std/core>"), Some(a));
93 assert_eq!(db.id_of("b.nova"), Some(b));
94 assert_eq!(db.id_of("c.nova"), None);
95 }
96}