1use std::{
2 collections::{HashMap, HashSet},
3 path::{Path, PathBuf},
4};
5
6#[derive(Debug, Default, Clone)]
7pub struct Dependencies {
8 dependencies: HashSet<PathBuf>,
9}
10
11pub struct IncludeHandler {
12 includes: Vec<String>,
13 directory_stack: HashSet<PathBuf>,
14 dependencies: Dependencies, path_remapping: HashMap<PathBuf, PathBuf>, }
17pub fn canonicalize(p: &Path) -> std::io::Result<PathBuf> {
22 fn __canonicalize(path: &Path, buf: &mut PathBuf) {
24 if path.is_absolute() {
25 buf.clear();
26 }
27 for part in path {
28 if part == ".." {
29 buf.pop();
30 } else if part != "." {
31 buf.push(part);
32 if let Ok(linkpath) = buf.read_link() {
33 buf.pop();
34 __canonicalize(&linkpath, buf);
35 }
36 }
37 }
38 }
39 let mut path = if p.is_absolute() {
40 PathBuf::new()
41 } else {
42 PathBuf::from(std::env::current_dir()?)
43 };
44 __canonicalize(p, &mut path);
45 Ok(path)
46}
47
48impl Dependencies {
49 pub fn new() -> Self {
50 Self {
51 dependencies: HashSet::new(),
52 }
53 }
54 pub fn add_dependency(&mut self, relative_path: PathBuf) {
55 self.dependencies.insert(
56 canonicalize(&relative_path).expect("Failed to convert dependency path to absolute"),
57 );
58 }
59 pub fn visit_dependencies<F: FnMut(&Path)>(&self, callback: &mut F) {
60 for dependency in &self.dependencies {
61 callback(&dependency);
62 }
63 }
64}
65
66impl IncludeHandler {
67 pub fn new(
68 file: &Path,
69 includes: Vec<String>,
70 path_remapping: HashMap<PathBuf, PathBuf>,
71 ) -> Self {
72 let mut includes_mut = includes;
74 let cwd = file.parent().unwrap();
75 let str = String::from(cwd.to_string_lossy());
76 includes_mut.push(str);
78 Self {
79 includes: includes_mut,
80 directory_stack: HashSet::new(),
81 dependencies: Dependencies::new(),
82 path_remapping: path_remapping,
83 }
84 }
85 pub fn search_in_includes(
86 &mut self,
87 relative_path: &Path,
88 include_callback: &mut dyn FnMut(&Path) -> Option<String>,
89 ) -> Option<(String, PathBuf)> {
90 match self.search_path_in_includes(relative_path) {
91 Some(absolute_path) => include_callback(&absolute_path).map(|e| (e, absolute_path)),
92 None => None,
93 }
94 }
95 pub fn search_path_in_includes(&mut self, relative_path: &Path) -> Option<PathBuf> {
96 self.search_path_in_includes_relative(relative_path)
97 .map(|e| canonicalize(&e).expect("Failed to convert relative path to absolute"))
98 }
99 pub fn search_path_in_includes_relative(&mut self, relative_path: &Path) -> Option<PathBuf> {
100 if relative_path.is_file() {
106 Some(PathBuf::from(relative_path))
107 } else {
108 for directory_stack in &self.directory_stack {
110 let path = Path::new(directory_stack).join(&relative_path);
111 if path.is_file() {
112 if let Some(parent) = path.parent() {
113 self.directory_stack.insert(canonicalize(parent).unwrap());
114 }
115 self.dependencies.add_dependency(path.clone());
116 return Some(path);
117 }
118 }
119 for include_path in &self.includes {
121 let path = Path::new(include_path).join(&relative_path);
122 if path.is_file() {
123 if let Some(parent) = path.parent() {
124 self.directory_stack.insert(canonicalize(parent).unwrap());
125 }
126 self.dependencies.add_dependency(path.clone());
127 return Some(path);
128 }
129 }
130 if let Some(target_path) =
132 Self::resolve_virtual_path(relative_path, &self.path_remapping)
133 {
134 if target_path.is_file() {
135 if let Some(parent) = target_path.parent() {
136 self.directory_stack.insert(canonicalize(parent).unwrap());
137 }
138 self.dependencies.add_dependency(target_path.clone());
139 return Some(target_path);
140 }
141 }
142 return None;
143 }
144 }
145 fn resolve_virtual_path(
146 virtual_path: &Path,
147 virtual_folders: &HashMap<PathBuf, PathBuf>,
148 ) -> Option<PathBuf> {
149 let virtual_path = if virtual_path.starts_with("./") || virtual_path.starts_with(".\\") {
154 let mut comp = virtual_path.components();
155 comp.next();
156 Path::new("/").join(comp.as_path())
157 } else {
158 PathBuf::from(virtual_path)
159 };
160 if virtual_path.starts_with("/") || virtual_path.starts_with("\\") {
161 for (virtual_folder, target_path) in virtual_folders {
163 let mut path_components = virtual_path.components();
164 let mut found = true;
165 for virtual_folder_component in virtual_folder.components() {
166 match path_components.next() {
167 Some(component) => {
168 if component != virtual_folder_component {
169 found = false;
170 break;
171 }
172 }
173 None => {
174 found = false;
175 break;
176 }
177 }
178 }
179 if found {
180 let resolved_path = target_path.join(path_components.as_path());
181 return Some(resolved_path.into());
182 }
183 }
184 None
185 } else {
186 None
187 }
188 }
189 pub fn get_dependencies(&self) -> &Dependencies {
190 return &self.dependencies;
191 }
192}