Skip to main content

dfir_lang/
pretty_span.rs

1//! Pretty, human-readable printing of [`proc_macro2::Span`]s.
2
3use std::path::Path;
4
5extern crate proc_macro;
6
7/// Helper struct which displays the span as `path:row:col` for human reading/IDE linking.
8/// Example: `dfir\tests\surface_syntax.rs:42:18`.
9pub struct PrettySpan(pub proc_macro2::Span);
10impl std::fmt::Display for PrettySpan {
11    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12        if proc_macro::is_available() {
13            use std::path::MAIN_SEPARATOR;
14
15            let span = self.0.unwrap();
16
17            let path = span.file();
18            let path = make_source_path_relative(&path);
19            let mut path_str = path.display().to_string();
20            if '/' != MAIN_SEPARATOR && path.is_relative() {
21                // Display relative paths using unix-style separators for consistency.
22                path_str = path_str.replace(MAIN_SEPARATOR, "/");
23            }
24
25            write!(
26                f,
27                "{}:{}:{}",
28                path_str,
29                span.start().line(),
30                span.start().column(),
31            )?;
32            return Ok(());
33        }
34
35        write!(
36            f,
37            "unknown:{}:{}",
38            self.0.start().line,
39            self.0.start().column
40        )
41    }
42}
43
44/// Helper struct which displays the span as `row:col` for human reading.
45pub struct PrettyRowCol(pub proc_macro2::Span);
46impl std::fmt::Display for PrettyRowCol {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        let span = self.0;
49        write!(f, "{}:{}", span.start().line, span.start().column)
50    }
51}
52
53/// Strip `DFIR_BASE_DIR` or `CARGO_MANIFEST_DIR` from the path prefix if possible.
54pub fn make_source_path_relative(source_path: &impl AsRef<Path>) -> &Path {
55    let source_path = source_path.as_ref();
56    std::env::var_os("DFIR_BASE_DIR")
57        .and_then(|base_dir| source_path.strip_prefix(base_dir).ok())
58        .or_else(|| {
59            let manifest_dir = std::env::var_os("CARGO_MANIFEST_DIR")?;
60            source_path.strip_prefix(manifest_dir).ok()
61        })
62        .unwrap_or(source_path)
63}