1use derive_more::Deref;
7
8use crate::{parse::*, rc::*, resolve::*, src_ref::*, syntax::*};
9use std::collections::HashMap;
10
11#[derive(Default, Deref)]
19pub struct Sources {
20 externals: Externals,
22
23 by_hash: HashMap<u64, usize>,
24 by_path: HashMap<std::path::PathBuf, usize>,
25 by_name: HashMap<QualifiedName, usize>,
26
27 root: Rc<SourceFile>,
29
30 #[deref]
32 pub source_files: Vec<Rc<SourceFile>>,
33
34 search_paths: Vec<std::path::PathBuf>,
36}
37
38impl Sources {
39 pub fn load(
43 root: Rc<SourceFile>,
44 search_paths: &[impl AsRef<std::path::Path>],
45 ) -> ResolveResult<Self> {
46 let mut source_files = Vec::new();
47 let mut by_name = HashMap::new();
48 let mut by_hash = HashMap::new();
49 let mut by_path = HashMap::new();
50
51 by_hash.insert(root.hash, 0);
52 by_path.insert(root.filename(), 0);
53 by_name.insert(root.name.clone(), 0);
54 source_files.push(root.clone());
55
56 let externals = Externals::new(search_paths)?;
58
59 log::trace!("Externals:\n{externals}");
60
61 externals
63 .iter()
64 .try_for_each(|(name, path)| -> Result<(), ParseErrorsWithSource> {
65 let (source_file, error) = SourceFile::load_with_name(path.clone(), name.clone());
66 let index = source_files.len();
67 by_hash.insert(source_file.hash, index);
68 by_path.insert(source_file.filename(), index);
69 by_name.insert(name.clone(), index);
70 source_files.push(source_file);
71 match error {
72 Some(error) => Err(error),
73 None => Ok(()),
74 }
75 })?;
76
77 Ok(Self {
78 externals,
79 root,
80 source_files,
81 by_hash,
82 by_path,
83 by_name,
84 search_paths: search_paths
85 .iter()
86 .map(|path| {
87 path.as_ref()
88 .canonicalize()
89 .unwrap_or_else(|_| panic!("valid path: {}", path.as_ref().display()))
90 })
91 .collect(),
92 })
93 }
94
95 pub fn root(&self) -> Rc<SourceFile> {
97 self.root.clone()
98 }
99
100 pub fn insert(&mut self, source_file: Rc<SourceFile>) {
102 let hash = source_file.hash;
103 let path = source_file.filename();
104 let name = source_file.name.clone();
105
106 let index = if let Some(index) = self.by_path.get(&path).copied() {
108 self.by_hash.remove(&hash);
109 self.by_name.remove(&name);
110 self.by_path.remove(&path);
111 if self.root.filename() == path {
112 self.root = source_file.clone();
113 }
114 self.source_files[index] = source_file;
115
116 index
117 } else {
118 self.source_files.push(source_file.clone());
119 self.source_files.len() - 1
120 };
121
122 self.by_hash.insert(hash, index);
123 self.by_path.insert(path, index);
124 self.by_name.insert(name, index);
125 }
126
127 pub fn generate_name_from_path(
129 &self,
130 file_path: &std::path::Path,
131 ) -> ResolveResult<QualifiedName> {
132 if self.root.filename() == file_path {
134 return Ok(QualifiedName::from_id(self.root.id()));
135 }
136
137 let path = if let Some(path) = self
139 .search_paths
140 .iter()
141 .find_map(|path| file_path.strip_prefix(path).ok())
142 {
143 path.with_extension("")
144 }
145 else if let Some(root_dir) = self.root_dir() {
147 if let Ok(path) = file_path.strip_prefix(root_dir) {
148 path.with_extension("")
149 } else {
150 return Err(ResolveError::InvalidPath(file_path.to_path_buf()));
151 }
152 } else {
153 return Err(ResolveError::InvalidPath(file_path.to_path_buf()));
154 };
155
156 let path = if let Ok(path) = path.strip_prefix(".test") {
158 path.to_path_buf()
159 } else {
160 path
161 };
162
163 let path = if path
165 .iter()
166 .next_back()
167 .map(|s| s.to_string_lossy().to_string())
168 == Some("mod".into())
169 {
170 path.parent()
171 } else {
172 Some(path.as_path())
173 };
174
175 if let Some(path) = path {
177 Ok(path
178 .iter()
179 .map(|name| Identifier::no_ref(name.to_string_lossy().as_ref()))
180 .collect())
181 } else {
182 Err(ResolveError::InvalidPath(file_path.to_path_buf()))
183 }
184 }
185
186 pub fn get_by_src_ref(&self, referrer: &impl SrcReferrer) -> ResolveResult<Rc<SourceFile>> {
188 self.get_by_hash(referrer.src_ref().source_hash())
189 }
190
191 pub fn ref_str(&self, referrer: &impl SrcReferrer) -> String {
193 format!(
194 "{}:{}",
195 self.get_by_src_ref(referrer)
196 .expect("Source file not found")
197 .filename_as_str(),
198 referrer.src_ref(),
199 )
200 }
201
202 pub fn get_by_path(&self, path: &std::path::Path) -> ResolveResult<Rc<SourceFile>> {
204 let path = path.to_path_buf();
205 if let Some(index) = self.by_path.get(&path) {
206 Ok(self.source_files[*index].clone())
207 } else {
208 Err(ResolveError::FileNotFound(path))
209 }
210 }
211
212 pub fn get_name_by_hash(&self, hash: u64) -> ResolveResult<&QualifiedName> {
214 match self.get_by_hash(hash) {
215 Ok(file) => self.externals.get_name(&file.filename()),
216 Err(err) => Err(err),
217 }
218 }
219
220 pub fn get_code(&self, referrer: &impl SrcReferrer) -> ResolveResult<String> {
222 Ok(self
223 .get_by_src_ref(referrer)?
224 .get_code(&referrer.src_ref())
225 .to_string())
226 }
227
228 pub fn get_by_name(&self, name: &QualifiedName) -> ResolveResult<Rc<SourceFile>> {
230 if let Some(index) = self.by_name.get(name) {
231 Ok(self.source_files[*index].clone())
232 } else {
233 match self.externals.fetch_external(name) {
235 Ok((name, path)) => {
236 if self.get_by_path(&path).is_err() {
237 return Err(ResolveError::SymbolMustBeLoaded(name, path));
238 }
239 }
240 Err(ResolveError::ExternalSymbolNotFound(_)) => (),
241 Err(err) => return Err(err),
242 }
243 Err(ResolveError::SymbolNotFound(name.clone()))
244 }
245 }
246
247 fn name_from_index(&self, index: usize) -> Option<QualifiedName> {
248 self.by_name
249 .iter()
250 .find(|(_, i)| **i == index)
251 .map(|(name, _)| name.clone())
252 }
253
254 pub fn search_paths(&self) -> &Vec<std::path::PathBuf> {
256 &self.search_paths
257 }
258
259 fn root_dir(&self) -> Option<std::path::PathBuf> {
260 self.root.filename().parent().map(|p| p.to_path_buf())
261 }
262
263 pub fn load_mod_file(
265 &mut self,
266 parent_path: impl AsRef<std::path::Path>,
267 id: &Identifier,
268 ) -> ResolveResult<Rc<SourceFile>> {
269 log::trace!(
270 "loading file: {:?} [{id}]",
271 parent_path.as_ref().canonicalize().expect("invalid path")
272 );
273 let file_path = find_mod_file_by_id(parent_path, id)?;
274 let name = self.generate_name_from_path(&file_path)?;
275 let (source_file, error) = SourceFile::load_with_name(&file_path, name);
276 self.insert(source_file.clone());
277 match error {
278 Some(error) => Err(error.into()),
279 None => Ok(source_file),
280 }
281 }
282
283 pub(super) fn update_file(
285 &mut self,
286 path: impl AsRef<std::path::Path>,
287 ) -> ResolveResult<ReplacedSourceFile> {
288 let path = path.as_ref().canonicalize()?.to_path_buf();
289 log::trace!("update_file: {path:?}");
290 if let Some(index) = self.by_path.get(&path).copied() {
291 let old = self.source_files[index].clone();
292 let name = old.name.clone();
293 let (new, error) = SourceFile::load_with_name(path, name);
294 self.insert(new.clone());
295 log::trace!("new sources:\n{self:?}");
296 match error {
297 Some(error) => Err(error.into()),
298 None => Ok(ReplacedSourceFile { new, old }),
299 }
300 } else {
301 Err(ResolveError::FileNotFound(path))
302 }
303 }
304}
305
306pub(super) struct ReplacedSourceFile {
307 pub(super) old: Rc<SourceFile>,
308 pub(super) new: Rc<SourceFile>,
309}
310
311pub trait GetSourceByHash {
313 fn get_by_hash(&self, hash: u64) -> ResolveResult<Rc<SourceFile>>;
315}
316
317impl GetSourceByHash for Sources {
318 fn get_by_hash(&self, hash: u64) -> ResolveResult<Rc<SourceFile>> {
320 if let Some(index) = self.by_hash.get(&hash) {
321 Ok(self.source_files[*index].clone())
322 } else if hash == 0 {
323 Err(ResolveError::NulHash)
324 } else {
325 Err(ResolveError::UnknownHash(hash))
326 }
327 }
328}
329
330impl std::fmt::Display for Sources {
331 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332 for (index, source_file) in self.source_files.iter().enumerate() {
333 let filename = source_file.filename_as_str();
334 let name = self
335 .name_from_index(index)
336 .unwrap_or(QualifiedName::no_ref(vec![]));
337 let hash = source_file.hash;
338 writeln!(f, "[{index}] {name} {hash:#x} {filename}")?;
339 }
340 Ok(())
341 }
342}
343
344impl std::fmt::Debug for Sources {
345 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346 for (index, source_file) in self.source_files.iter().enumerate() {
347 let filename = source_file.filename_as_str();
348 let name = self
349 .name_from_index(index)
350 .unwrap_or(QualifiedName::no_ref(vec![]));
351 let hash = source_file.hash;
352 writeln!(f, "[{index}] {name:?} {hash:#x} {filename}")?;
353 }
354 Ok(())
355 }
356}