sway_types/
source_engine.rs

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