module_path_extractor/
lib.rs1#![feature(proc_macro_span)]
2
3use proc_macro::Span;
4extern crate proc_macro;
5
6use std::fs;
7
8pub fn get_source_info() -> Option<(String, usize)> {
10 let span = Span::call_site();
11 let file_path = span.source_file().path();
12 let file_path = file_path.to_string_lossy().to_string();
13 let line_number = span.start().line(); Some((file_path, line_number))
15}
16
17pub fn find_module_path(file_path: &str, line_number: usize) -> Option<String> {
19 let content = fs::read_to_string(file_path).ok()?; let mut module_path = Vec::new();
22 let mut last_inline_module: Option<String> = None;
23
24 for (i, line) in content.lines().enumerate() {
25 if i >= line_number {
26 break;
27 }
28
29 let trimmed = line.trim();
30
31 if let Some(mod_name) = trimmed.strip_prefix("mod ") {
32 if let Some(mod_name) = mod_name.strip_suffix('{') {
33 let mod_name = mod_name.trim().to_string();
34 last_inline_module = Some(mod_name.clone());
35 module_path.clear();
36 module_path.push(mod_name);
37 }
38 } else if let Some(pub_mod_name) = trimmed.strip_prefix("pub mod ") {
39 if let Some(pub_mod_name) = pub_mod_name.strip_suffix('{') {
40 let pub_mod_name = pub_mod_name.trim().to_string();
41 last_inline_module = Some(pub_mod_name.clone());
42 module_path.clear();
43 module_path.push(pub_mod_name);
44 }
45 }
46 }
47
48 if !module_path.is_empty() {
49 return Some(module_path.join("::").to_string());
50 }
51
52 Some(generate_pseudo_module_path(file_path, last_inline_module))
54}
55
56pub fn generate_pseudo_module_path(file_path: &str, inline_module: Option<String>) -> String {
58 let filename = file_path.rsplit('/').next().unwrap_or(file_path);
59 let filename = filename.split('.').next().unwrap_or(filename);
60
61 let parent_dir = file_path.rsplit_once('/').map(|(dir, _)| dir).unwrap_or("");
62
63 let parent_dir = parent_dir.replace("/", "::");
64
65 let parent_dir = parent_dir.strip_prefix("src::").unwrap_or(&parent_dir);
66
67 if filename == "main" || filename == "lib" {
68 return "crate".to_string();
69 }
70
71 if let Some(inline_mod) = inline_module {
72 return inline_mod.to_string();
73 }
74
75 if parent_dir.is_empty() {
76 filename.to_string()
77 } else {
78 format!("{}::{}", parent_dir, filename)
79 }
80}
81
82pub fn get_pseudo_module_path() -> String {
84 get_source_info()
85 .and_then(|(file, line)| find_module_path(&file, line))
86 .unwrap_or_else(|| "unknown".to_string())
87}