1use std::path::{Path, PathBuf};
2
3pub mod parse_normal;
4pub mod parse_trace;
5pub mod trace_macros;
6
7pub fn normalize_tokens(s: &str) -> String {
13 let chars: Vec<char> = s.chars().collect();
14 let mut result = String::with_capacity(chars.len());
15
16 fn is_punct(c: char) -> bool {
17 !c.is_alphanumeric() && c != '_' && c != '"' && c != '\'' && !c.is_whitespace()
18 }
19
20 let mut i = 0;
21 while i < chars.len() {
22 let c = chars[i];
23 if c.is_whitespace() {
24 let prev = result.chars().last();
25 while i < chars.len() && chars[i].is_whitespace() {
26 i += 1;
27 }
28 let next = chars.get(i).copied();
29 let prev_is_punct = prev.is_none_or(is_punct);
30 let next_is_punct = next.is_none_or(is_punct);
31 if !prev_is_punct && !next_is_punct {
32 result.push(' ');
33 }
34 } else {
35 match c {
36 '{' | '[' => result.push('('),
37 '}' | ']' => result.push(')'),
38 _ => result.push(c),
39 }
40 i += 1;
41 }
42 }
43
44 result
45}
46
47#[cfg(target_os = "macos")]
48const HOOK_LIB_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libmacra_hook.dylib"));
49
50#[cfg(target_os = "windows")]
51const HOOK_LIB_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/macra_hook.dll"));
52
53#[cfg(not(any(target_os = "macos", target_os = "windows")))]
54const HOOK_LIB_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libmacra_hook.so"));
55
56#[cfg(target_os = "windows")]
57const WRAPPER_EXE_BYTES: &[u8] =
58 include_bytes!(concat!(env!("OUT_DIR"), "/macra-rustc-wrapper.exe"));
59
60fn ensure_hook_lib() -> Option<PathBuf> {
63 let lib_name = if cfg!(target_os = "macos") {
64 "libmacra_hook.dylib"
65 } else if cfg!(target_os = "windows") {
66 "macra_hook.dll"
67 } else {
68 "libmacra_hook.so"
69 };
70 let arch = std::env::consts::ARCH;
71
72 let version = env!("CARGO_PKG_VERSION");
73 let file_name = if cfg!(target_os = "windows") {
74 format!("macra_hook-{}-{}.dll", version, arch)
75 } else if cfg!(target_os = "macos") {
76 format!("libmacra_hook-{}-{}.dylib", version, arch)
77 } else {
78 format!("libmacra_hook-{}-{}.so", version, arch)
79 };
80
81 let cache_dir = dirs_cache()?;
82 let dest = cache_dir.join(&file_name);
83 let dest_plain = if cfg!(target_os = "windows") {
84 cache_dir.join(format!("macra_hook-{}.dll", arch))
85 } else if cfg!(target_os = "macos") {
86 cache_dir.join(format!("libmacra_hook-{}.dylib", arch))
87 } else {
88 cache_dir.join(format!("libmacra_hook-{}.so", arch))
89 };
90
91 if let Ok(meta) = std::fs::metadata(&dest) {
93 if meta.len() == HOOK_LIB_BYTES.len() as u64 {
94 if !dest_plain.exists() {
96 let _ = std::fs::copy(&dest, &dest_plain);
97 }
98 let _ = std::fs::copy(&dest, cache_dir.join(lib_name));
100 return Some(dest_plain);
101 }
102 }
103
104 if std::fs::create_dir_all(&cache_dir).is_err() {
106 return None;
107 }
108 if std::fs::write(&dest, HOOK_LIB_BYTES).is_err() {
109 return None;
110 }
111 let _ = std::fs::copy(&dest, &dest_plain);
112 let _ = std::fs::copy(&dest, cache_dir.join(lib_name));
113
114 Some(dest_plain)
115}
116
117#[cfg(target_os = "windows")]
119fn ensure_wrapper_exe() -> Option<PathBuf> {
120 let version = env!("CARGO_PKG_VERSION");
121 let arch = std::env::consts::ARCH;
122 let file_name = format!("macra-rustc-wrapper-{}-{}.exe", version, arch);
123
124 let cache_dir = dirs_cache()?;
125 let dest = cache_dir.join(&file_name);
126 let dest_plain = cache_dir.join(format!("macra-rustc-wrapper-{}.exe", arch));
127
128 if let Ok(meta) = std::fs::metadata(&dest) {
129 if meta.len() == WRAPPER_EXE_BYTES.len() as u64 {
130 if !dest_plain.exists() {
131 let _ = std::fs::copy(&dest, &dest_plain);
132 }
133 return Some(dest_plain);
134 }
135 }
136
137 if std::fs::create_dir_all(&cache_dir).is_err() {
138 return None;
139 }
140 if std::fs::write(&dest, WRAPPER_EXE_BYTES).is_err() {
141 return None;
142 }
143 let _ = std::fs::copy(&dest, &dest_plain);
144
145 Some(dest_plain)
146}
147
148#[cfg(target_os = "windows")]
150pub fn find_wrapper_exe(current_exe: Option<&Path>) -> Option<PathBuf> {
151 let exe_name = "macra-rustc-wrapper.exe";
152 let arch = std::env::consts::ARCH;
153 let arch_name = format!("macra-rustc-wrapper-{}.exe", arch);
154
155 if let Some(exe) = current_exe {
156 if let Some(dir) = exe.parent() {
157 let wrapper = dir.join(exe_name);
158 if wrapper.exists() {
159 return Some(wrapper);
160 }
161 }
162 }
163
164 let paths = [
165 PathBuf::from(format!("./target/debug/{}", arch_name)),
166 PathBuf::from(format!("./target/release/{}", arch_name)),
167 PathBuf::from(format!("./target/debug/{}", exe_name)),
168 PathBuf::from(format!("./target/release/{}", exe_name)),
169 ];
170
171 for path in paths {
172 if path.exists() {
173 return Some(path);
174 }
175 }
176
177 ensure_wrapper_exe()
178}
179
180fn dirs_cache() -> Option<PathBuf> {
182 #[cfg(target_os = "windows")]
183 {
184 std::env::var_os("LOCALAPPDATA").map(|d| PathBuf::from(d).join("cargo-macra"))
185 }
186 #[cfg(not(target_os = "windows"))]
187 {
188 std::env::var_os("XDG_CACHE_HOME")
189 .map(PathBuf::from)
190 .or_else(|| std::env::var_os("HOME").map(|h| PathBuf::from(h).join(".cache")))
191 .map(|d| d.join("cargo-macra"))
192 }
193}
194
195pub fn find_hook_lib(current_exe: Option<&Path>) -> Option<PathBuf> {
199 let lib_name = if cfg!(target_os = "macos") {
200 "libmacra_hook.dylib"
201 } else if cfg!(target_os = "windows") {
202 "macra_hook.dll"
203 } else {
204 "libmacra_hook.so"
205 };
206 let arch = std::env::consts::ARCH;
207 let arch_name = if cfg!(target_os = "windows") {
208 format!("macra_hook-{}.dll", arch)
209 } else if cfg!(target_os = "macos") {
210 format!("libmacra_hook-{}.dylib", arch)
211 } else {
212 format!("libmacra_hook-{}.so", arch)
213 };
214
215 if let Some(exe) = current_exe {
216 if let Some(dir) = exe.parent() {
217 let hook_lib = dir.join(lib_name);
218 if hook_lib.exists() {
219 return Some(hook_lib);
220 }
221 }
222 }
223
224 let paths = [
225 PathBuf::from(format!("./target/debug/{}", arch_name)),
226 PathBuf::from(format!("./target/release/{}", arch_name)),
227 PathBuf::from(format!("./target/debug/{}", lib_name)),
228 PathBuf::from(format!("./target/release/{}", lib_name)),
229 ];
230
231 for path in paths {
232 if path.exists() {
233 return Some(path);
234 }
235 }
236
237 ensure_hook_lib()
239}