code_path/
lib.rs

1#![doc = include_str!("../README.md")]
2#![forbid(unsafe_code)]
3#![deny(
4    clippy::all,
5    clippy::complexity,
6    clippy::expect_used,
7    clippy::indexing_slicing,
8    clippy::panic,
9    clippy::pedantic,
10    clippy::perf,
11    clippy::style,
12    clippy::suspicious,
13    clippy::todo,
14    clippy::unimplemented,
15    clippy::unwrap_used,
16    future_incompatible,
17    keyword_idents,
18    let_underscore,
19    missing_docs,
20    nonstandard_style,
21    refining_impl_trait,
22    rust_2018_compatibility,
23    rust_2018_idioms,
24    rust_2021_compatibility,
25    rust_2024_compatibility,
26    unreachable_pub,
27    unused
28)]
29#![warn(clippy::nursery)]
30use std::{
31    fmt,
32    ops::{Deref, DerefMut},
33};
34
35/// Represents path in the code
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct CodePath(String);
38
39impl fmt::Display for CodePath {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        write!(f, "{}", self.0)
42    }
43}
44
45impl From<&str> for CodePath {
46    fn from(s: &str) -> Self {
47        Self(s.into())
48    }
49}
50
51impl From<String> for CodePath {
52    fn from(s: String) -> Self {
53        Self(s)
54    }
55}
56
57impl From<CodePath> for String {
58    fn from(val: CodePath) -> Self {
59        val.0
60    }
61}
62
63impl Deref for CodePath {
64    type Target = String;
65
66    fn deref(&self) -> &Self::Target {
67        &self.0
68    }
69}
70
71impl DerefMut for CodePath {
72    fn deref_mut(&mut self) -> &mut Self::Target {
73        &mut self.0
74    }
75}
76
77/// Returns the current code scope with location, e.g.
78/// `code_path::tests::scope_path::foo::bar, src/lib.rs:80:17`
79#[macro_export]
80macro_rules! code_path {
81    () => {
82        $crate::CodePath::from(format!(
83            "{}, {}",
84            $crate::code_scope!(),
85            $crate::code_loc!()
86        ))
87    };
88}
89
90/// Returns the current scope path, e.g. `my_crate::my_module::my_function`)
91#[macro_export]
92macro_rules! code_scope {
93    () => {{
94        const fn f() {}
95        fn type_name_of<T>(_: T) -> &'static str {
96            ::std::any::type_name::<T>()
97        }
98        type_name_of(f)
99            .strip_suffix("::f")
100            .unwrap_or_default()
101            .trim_end_matches("::{{closure}}")
102    }};
103}
104
105/// Returns the code location: `file_name:line:column`
106#[macro_export]
107macro_rules! code_loc {
108    () => {
109        concat!(file!(), ":", line!(), ":", column!())
110    };
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn nesting() {
119        fn foo() -> &'static str {
120            fn bar() -> &'static str {
121                code_scope!()
122            }
123            bar()
124        }
125
126        assert_eq!(foo(), "code_path::tests::nesting::foo::bar");
127    }
128
129    #[test]
130    fn ending_cloures() {
131        fn foo() -> &'static str {
132            #[allow(clippy::redundant_closure_call)]
133            (|| (|| code_scope!())())()
134        }
135        assert_eq!(foo(), "code_path::tests::ending_cloures::foo");
136    }
137}