q_debug/
macros.rs

1/// Expands to the path of the function within which it was invoked e.g.
2/// `"crate::module::function::"`
3#[macro_export]
4macro_rules! function {
5    () => {{
6        // This works by defining a function within the call site and using
7        // (abusing) `std::any::type_name` to retrieve it's full path.
8        //
9        // Note: it is possible that this may break as the docs for
10        // `std::any::type_name`` state:
11        //
12        // > This is intended for diagnostic use. The exact contents and
13        // > format of the string are not specified, other than being a
14        // > best-effort description of the type
15        //
16        // and
17        // > ... output may change between versions of the compiler.
18        //
19        // I'm ok with this because `q` is a low-risk crate (e.g. it's a "cool"
20        // debugging tool but if it breaks it won't be the end of the world),
21        // it works for now and I can't (currently) think of a better way to
22        // do this without more "heavy-weight" methods e.g. parsing backtraces
23        // or proc macros.
24        fn f() {}
25        fn type_name_of<T>(_: T) -> &'static str {
26            std::any::type_name::<T>()
27        }
28        let name = type_name_of(f); // `"crate::module::function::f"`
29        &name[..name.len() - 3] // `"crate::module::function::"`
30    }};
31}
32
33#[macro_export]
34macro_rules! loc {
35    () => {
36        $crate::LogLocation {
37            file_path: file!().to_string(),
38            func_path: function!().to_string(),
39            lineno: line!(),
40            thread_id: std::thread::current().id(),
41        }
42    };
43}
44
45// TODO: Variadic arguments e.g. `q!("Hello", 1, foo(2))` -> `"> \"Hello\", 1, foo(2) = 3"`
46
47#[macro_export]
48macro_rules! q {
49    // Note: `unwrap` is used when accessing the the global `Logger` mutex as
50    // trying to recover from lock poisoning is not suitable for this use case.
51    () => {
52        $crate::LOGGER.write().unwrap().q(loc!());
53    };
54
55    ($x:literal) => {{
56        let val = $x;
57        $crate::LOGGER.write().unwrap().q_literal(&val, loc!());
58        val
59    }};
60
61    ($x:expr) => {{
62        let val = $x;
63        $crate::LOGGER
64            .write()
65            .unwrap()
66            .q_expr(&val, stringify!($x), loc!());
67        val
68    }};
69}
70
71#[cfg(test)]
72mod tests {
73
74    #[test]
75    fn test_function() {
76        assert_eq!(function!(), "q_debug::macros::tests::test_function");
77
78        struct Foo {
79            bar: String,
80        }
81
82        impl Foo {
83            pub fn new() -> Self {
84                Foo {
85                    bar: function!().to_string(),
86                }
87            }
88        }
89
90        assert_eq!(
91            Foo::new().bar,
92            "q_debug::macros::tests::test_function::Foo::new"
93        );
94    }
95}