microcad_lang/resolve/
sources.rs1use 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<(), ParseError> {
65 let source_file = 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 Ok(())
72 })?;
73
74 Ok(Self {
75 externals,
76 root,
77 source_files,
78 by_hash,
79 by_path,
80 by_name,
81 search_paths: search_paths
82 .iter()
83 .map(|path| path.as_ref().canonicalize().expect("valid path"))
84 .collect(),
85 })
86 }
87
88 pub fn root(&self) -> Rc<SourceFile> {
90 self.root.clone()
91 }
92
93 pub fn insert(&mut self, source_file: Rc<SourceFile>) {
95 let index = self.source_files.len();
96 self.source_files.push(source_file.clone());
97 self.by_hash.insert(source_file.hash, index);
98 self.by_path.insert(source_file.filename(), index);
99 self.by_name.insert(source_file.name.clone(), index);
100 }
101
102 pub fn generate_name_from_path(
104 &self,
105 file_path: &std::path::Path,
106 ) -> ResolveResult<QualifiedName> {
107 if self.root.filename() == file_path {
109 return Ok(QualifiedName::from_id(self.root.id()));
110 }
111
112 let path = if let Some(path) = self
114 .search_paths
115 .iter()
116 .find_map(|path| file_path.strip_prefix(path).ok())
117 {
118 path.with_extension("")
119 }
120 else if let Some(root_dir) = self.root_dir() {
122 if let Ok(path) = file_path.strip_prefix(root_dir) {
123 path.with_extension("")
124 } else {
125 return Err(ResolveError::InvalidPath(file_path.to_path_buf()));
126 }
127 } else {
128 return Err(ResolveError::InvalidPath(file_path.to_path_buf()));
129 };
130
131 let path = if path
133 .iter()
134 .next_back()
135 .map(|s| s.to_string_lossy().to_string())
136 == Some("mod".into())
137 {
138 path.parent()
139 } else {
140 Some(path.as_path())
141 };
142
143 if let Some(path) = path {
145 Ok(path
146 .iter()
147 .map(|name| Identifier::no_ref(name.to_string_lossy().as_ref()))
148 .collect())
149 } else {
150 Err(ResolveError::InvalidPath(file_path.to_path_buf()))
151 }
152 }
153
154 pub fn get_by_src_ref(&self, referrer: &impl SrcReferrer) -> ResolveResult<Rc<SourceFile>> {
156 self.get_by_hash(referrer.src_ref().source_hash())
157 }
158
159 pub fn ref_str(&self, referrer: &impl SrcReferrer) -> String {
161 format!(
162 "{}:{}",
163 self.get_by_src_ref(referrer)
164 .expect("Source file not found")
165 .filename_as_str(),
166 referrer.src_ref(),
167 )
168 }
169
170 pub fn get_by_path(&self, path: &std::path::Path) -> ResolveResult<Rc<SourceFile>> {
172 let path = path.to_path_buf();
173 if let Some(index) = self.by_path.get(&path) {
174 Ok(self.source_files[*index].clone())
175 } else {
176 Err(ResolveError::FileNotFound(path))
177 }
178 }
179
180 pub fn get_name_by_hash(&self, hash: u64) -> ResolveResult<&QualifiedName> {
182 match self.get_by_hash(hash) {
183 Ok(file) => self.externals.get_name(&file.filename()),
184 Err(err) => Err(err),
185 }
186 }
187
188 pub fn get_by_name(&self, name: &QualifiedName) -> ResolveResult<Rc<SourceFile>> {
190 if let Some(index) = self.by_name.get(name) {
191 Ok(self.source_files[*index].clone())
192 } else {
193 match self.externals.fetch_external(name) {
195 Ok((name, path)) => {
196 if self.get_by_path(&path).is_err() {
197 return Err(ResolveError::SymbolMustBeLoaded(name, path));
198 }
199 }
200 Err(ResolveError::ExternalSymbolNotFound(_)) => (),
201 Err(err) => return Err(err),
202 }
203 Err(ResolveError::SymbolNotFound(name.clone()))
204 }
205 }
206
207 fn name_from_index(&self, index: usize) -> Option<QualifiedName> {
208 self.by_name
209 .iter()
210 .find(|(_, i)| **i == index)
211 .map(|(name, _)| name.clone())
212 }
213
214 pub fn search_paths(&self) -> &Vec<std::path::PathBuf> {
216 &self.search_paths
217 }
218
219 fn root_dir(&self) -> Option<std::path::PathBuf> {
220 self.root.filename().parent().map(|p| p.to_path_buf())
221 }
222
223 pub fn load_file(
225 &mut self,
226 parent_path: impl AsRef<std::path::Path>,
227 id: &Identifier,
228 ) -> ResolveResult<Rc<SourceFile>> {
229 log::trace!("load_file: {:?} {id}", parent_path.as_ref());
230 let file_path = find_mod_file_by_id(parent_path, id)?;
231 let name = self.generate_name_from_path(&file_path)?;
232 let source_file = SourceFile::load_with_name(&file_path, name)?;
233 self.insert(source_file.clone());
234 Ok(source_file)
235 }
236}
237
238pub trait GetSourceByHash {
240 fn get_by_hash(&self, hash: u64) -> ResolveResult<Rc<SourceFile>>;
242}
243
244impl GetSourceByHash for Sources {
245 fn get_by_hash(&self, hash: u64) -> ResolveResult<Rc<SourceFile>> {
247 if let Some(index) = self.by_hash.get(&hash) {
248 Ok(self.source_files[*index].clone())
249 } else if hash == 0 {
250 Err(ResolveError::NulHash)
251 } else {
252 Err(ResolveError::UnknownHash(hash))
253 }
254 }
255}
256
257impl std::fmt::Display for Sources {
258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259 for (index, source_file) in self.source_files.iter().enumerate() {
260 let filename = source_file.filename_as_str();
261 let name = self
262 .name_from_index(index)
263 .unwrap_or(QualifiedName::no_ref(vec![]));
264 let hash = source_file.hash;
265 writeln!(f, "[{index}] {name} {hash:#x} {filename}")?;
266 }
267 Ok(())
268 }
269}
270
271impl std::fmt::Debug for Sources {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 for (index, source_file) in self.source_files.iter().enumerate() {
274 let filename = source_file.filename_as_str();
275 let name = self
276 .name_from_index(index)
277 .unwrap_or(QualifiedName::no_ref(vec![]));
278 let hash = source_file.hash;
279 writeln!(f, "[{index}] {name:?} {hash:#x} {filename}")?;
280 }
281 Ok(())
282 }
283}