Skip to main content

libperl_macrogen/
source.rs

1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3
4/// ファイル識別子
5#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
6pub struct FileId(u32);
7
8impl FileId {
9    /// 内部IDを取得(デバッグ用)
10    pub fn as_u32(self) -> u32 {
11        self.0
12    }
13}
14
15/// ソース位置
16#[derive(Debug, Clone, Default, PartialEq, Eq)]
17pub struct SourceLocation {
18    pub file_id: FileId,
19    pub line: u32,
20    pub column: u32,
21}
22
23impl SourceLocation {
24    /// 新しいソース位置を作成
25    pub fn new(file_id: FileId, line: u32, column: u32) -> Self {
26        Self {
27            file_id,
28            line,
29            column,
30        }
31    }
32}
33
34/// ファイルレジストリ
35#[derive(Debug, Default, Clone)]
36pub struct FileRegistry {
37    paths: Vec<PathBuf>,
38    path_to_id: HashMap<PathBuf, FileId>,
39}
40
41impl FileRegistry {
42    /// 新しいレジストリを作成
43    pub fn new() -> Self {
44        Self {
45            paths: Vec::new(),
46            path_to_id: HashMap::new(),
47        }
48    }
49
50    /// パスを登録してIDを返す
51    pub fn register(&mut self, path: PathBuf) -> FileId {
52        if let Some(&id) = self.path_to_id.get(&path) {
53            return id;
54        }
55        let id = FileId(self.paths.len() as u32);
56        self.path_to_id.insert(path.clone(), id);
57        self.paths.push(path);
58        id
59    }
60
61    /// IDからパスを取得
62    pub fn get_path(&self, id: FileId) -> &Path {
63        &self.paths[id.0 as usize]
64    }
65
66    /// IDからパスを取得(範囲外なら None)
67    pub fn try_get_path(&self, id: FileId) -> Option<&Path> {
68        self.paths.get(id.0 as usize).map(|p| p.as_path())
69    }
70
71    /// 登録されているファイル数を返す
72    pub fn len(&self) -> usize {
73        self.paths.len()
74    }
75
76    /// レジストリが空かどうか
77    pub fn is_empty(&self) -> bool {
78        self.paths.is_empty()
79    }
80
81    /// 登録されたファイルをイテレート
82    pub fn iter(&self) -> impl Iterator<Item = (FileId, &Path)> {
83        self.paths
84            .iter()
85            .enumerate()
86            .map(|(i, p)| (FileId(i as u32), p.as_path()))
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_file_registry_register() {
96        let mut registry = FileRegistry::new();
97        let id1 = registry.register(PathBuf::from("/path/to/file1.c"));
98        let id2 = registry.register(PathBuf::from("/path/to/file2.c"));
99
100        assert_ne!(id1, id2);
101        assert_eq!(registry.get_path(id1), Path::new("/path/to/file1.c"));
102        assert_eq!(registry.get_path(id2), Path::new("/path/to/file2.c"));
103    }
104
105    #[test]
106    fn test_file_registry_same_path() {
107        let mut registry = FileRegistry::new();
108        let id1 = registry.register(PathBuf::from("/path/to/file.c"));
109        let id2 = registry.register(PathBuf::from("/path/to/file.c"));
110
111        assert_eq!(id1, id2);
112        assert_eq!(registry.len(), 1);
113    }
114
115    #[test]
116    fn test_source_location() {
117        let loc = SourceLocation::new(FileId(0), 10, 5);
118        assert_eq!(loc.line, 10);
119        assert_eq!(loc.column, 5);
120    }
121}