sway_types/
source_engine.rs

1use crate::{ProgramId, SourceId};
2use parking_lot::RwLock;
3use std::{
4    collections::{BTreeSet, HashMap},
5    path::PathBuf,
6};
7
8/// The Source Engine manages a relationship between file paths and their corresponding
9/// integer-based source IDs. Additionally, it maintains the reverse - a map that traces
10/// back from a source ID to its original file path. The primary objective of this
11/// system is to enable clients that need to reference a file path to do so using an
12/// integer-based ID. This numeric representation can be stored more efficiently as
13/// a key in a hashmap.
14/// The Source Engine is designed to be thread-safe. Its internal structures are
15/// secured by the RwLock mechanism. This allows its functions to be invoked using
16/// a straightforward non-mutable reference, ensuring safe concurrent access.
17#[derive(Debug, Default)]
18pub struct SourceEngine {
19    next_source_id: RwLock<u32>,
20    path_to_source_map: RwLock<HashMap<PathBuf, SourceId>>,
21    source_to_path_map: RwLock<HashMap<SourceId, PathBuf>>,
22    next_program_id: RwLock<u16>,
23    manifest_path_to_program_map: RwLock<HashMap<PathBuf, ProgramId>>,
24    module_to_sources_map: RwLock<HashMap<ProgramId, BTreeSet<SourceId>>>,
25}
26
27impl Clone for SourceEngine {
28    fn clone(&self) -> Self {
29        SourceEngine {
30            next_source_id: RwLock::new(*self.next_source_id.read()),
31            path_to_source_map: RwLock::new(self.path_to_source_map.read().clone()),
32            source_to_path_map: RwLock::new(self.source_to_path_map.read().clone()),
33            next_program_id: RwLock::new(*self.next_program_id.read()),
34            manifest_path_to_program_map: RwLock::new(
35                self.manifest_path_to_program_map.read().clone(),
36            ),
37            module_to_sources_map: RwLock::new(self.module_to_sources_map.read().clone()),
38        }
39    }
40}
41
42impl SourceEngine {
43    const AUTOGENERATED_PATH: &'static str = "<autogenerated>";
44
45    pub fn is_span_in_autogenerated(&self, span: &crate::Span) -> Option<bool> {
46        span.source_id().map(|s| self.is_source_id_autogenerated(s))
47    }
48
49    pub fn is_source_id_autogenerated(&self, source_id: &SourceId) -> bool {
50        self.get_path(source_id).starts_with("<autogenerated>")
51    }
52
53    /// This function retrieves an integer-based source ID for a provided path buffer.
54    /// If an ID already exists for the given path, the function will return that
55    /// existing ID. If not, a new ID will be created.
56    pub fn get_source_id(&self, path: &PathBuf) -> SourceId {
57        {
58            let source_map = self.path_to_source_map.read();
59            if source_map.contains_key(path) {
60                return source_map.get(path).copied().unwrap();
61            }
62        }
63
64        let program_id = self.get_or_create_program_id_from_manifest_path(path);
65        self.get_source_id_with_program_id(path, program_id)
66    }
67
68    pub fn get_source_id_with_program_id(&self, path: &PathBuf, program_id: ProgramId) -> SourceId {
69        {
70            let source_map = self.path_to_source_map.read();
71            if source_map.contains_key(path) {
72                return source_map.get(path).copied().unwrap();
73            }
74        }
75
76        let source_id = SourceId::new(program_id.0, *self.next_source_id.read());
77        {
78            let mut next_id = self.next_source_id.write();
79            *next_id += 1;
80
81            let mut source_map = self.path_to_source_map.write();
82            source_map.insert(path.clone(), source_id);
83
84            let mut path_map = self.source_to_path_map.write();
85            path_map.insert(source_id, path.clone());
86        }
87
88        let mut module_map = self.module_to_sources_map.write();
89        module_map.entry(program_id).or_default().insert(source_id);
90
91        source_id
92    }
93
94    pub fn get_autogenerated_source_id(&self, program_id: ProgramId) -> SourceId {
95        self.get_source_id_with_program_id(&Self::AUTOGENERATED_PATH.into(), program_id)
96    }
97
98    /// This function provides the file path corresponding to a specified source ID.
99    pub fn get_path(&self, source_id: &SourceId) -> PathBuf {
100        self.source_to_path_map
101            .read()
102            .get(source_id)
103            .unwrap()
104            .clone()
105    }
106
107    /// This function provides the [ProgramId] corresponding to a specified manifest file path.
108    pub fn get_program_id_from_manifest_path(&self, path: &PathBuf) -> Option<ProgramId> {
109        let manifest_path = sway_utils::find_parent_manifest_dir(path).unwrap_or(path.clone());
110        self.manifest_path_to_program_map
111            .read()
112            .get(&manifest_path)
113            .copied()
114    }
115
116    pub fn get_or_create_program_id_from_manifest_path(&self, path: &PathBuf) -> ProgramId {
117        let manifest_path = sway_utils::find_parent_manifest_dir(path).unwrap_or(path.clone());
118        let mut module_map = self.manifest_path_to_program_map.write();
119        *module_map.entry(manifest_path.clone()).or_insert_with(|| {
120            let mut next_id = self.next_program_id.write();
121            *next_id += 1;
122            ProgramId::new(*next_id)
123        })
124    }
125
126    /// Returns the [PathBuf] associated with the provided [ProgramId], if it exists in the manifest_path_to_program_map.
127    pub fn get_manifest_path_from_program_id(&self, program_id: &ProgramId) -> Option<PathBuf> {
128        let path_to_module_map = self.manifest_path_to_program_map.read();
129        path_to_module_map
130            .iter()
131            .find(|(_, &id)| id == *program_id)
132            .map(|(path, _)| path.clone())
133    }
134
135    /// This function provides the file name (with extension) corresponding to a specified source ID.
136    pub fn get_file_name(&self, source_id: &SourceId) -> Option<String> {
137        self.get_path(source_id)
138            .as_path()
139            .file_name()
140            .map(|file_name| file_name.to_string_lossy())
141            .map(|file_name| file_name.to_string())
142    }
143
144    pub fn all_files(&self) -> Vec<PathBuf> {
145        let s = self.source_to_path_map.read();
146        let mut v = s.values().cloned().collect::<Vec<_>>();
147        v.sort();
148        v
149    }
150
151    pub fn get_source_ids_from_program_id(
152        &self,
153        program_id: ProgramId,
154    ) -> Option<BTreeSet<SourceId>> {
155        let s = self.module_to_sources_map.read();
156        s.get(&program_id).cloned()
157    }
158}