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