predawn_core/
location.rs

1use std::{fmt, panic};
2
3use macro_v::macro_v;
4use snafu::GenerateImplicitData;
5
6#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
7pub struct Location {
8    file: &'static str,
9    line: u32,
10    column: u32,
11}
12
13impl Location {
14    #[doc(hidden)]
15    #[inline]
16    pub const fn new(file: &'static str, line: u32, column: u32) -> Self {
17        Self { file, line, column }
18    }
19
20    #[track_caller]
21    #[inline]
22    pub const fn caller() -> Self {
23        Self::from_std(panic::Location::caller())
24    }
25
26    #[inline]
27    pub const fn file(&self) -> &'static str {
28        self.file
29    }
30
31    #[inline]
32    pub const fn line(&self) -> u32 {
33        self.line
34    }
35
36    #[inline]
37    pub const fn column(&self) -> u32 {
38        self.column
39    }
40
41    #[inline]
42    pub const fn from_std(location: &'static panic::Location<'_>) -> Self {
43        Self {
44            file: location.file(),
45            line: location.line(),
46            column: location.column(),
47        }
48    }
49}
50
51impl fmt::Display for Location {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        write!(f, "{}:{}:{}", self.file, self.line, self.column)
54    }
55}
56
57impl<'a> From<&'static panic::Location<'a>> for Location {
58    #[inline]
59    fn from(location: &'static panic::Location<'a>) -> Self {
60        Self::from_std(location)
61    }
62}
63
64/// Constructs a `Location` that is unaffected by `#[track_caller]`
65#[macro_v(pub)]
66macro_rules! location {
67    () => {
68        $crate::location::Location::new(file!(), line!(), column!())
69    };
70}
71
72impl GenerateImplicitData for Location {
73    #[track_caller]
74    #[inline]
75    fn generate() -> Self {
76        Self::caller()
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn impact_of_track_caller_in_location() {
86        let Tuple {
87            from_std: loc_from_std_by_fn,
88            from_crate: loc_from_crate_by_fn,
89        } = location_by_fn();
90
91        assert_eq!(loc_from_std_by_fn, loc_from_crate_by_fn);
92
93        let Tuple {
94            from_std: loc_from_std_by_macro,
95            from_crate: loc_from_crate_by_macro,
96        } = location_by_macro();
97
98        assert_ne!(loc_from_std_by_macro, loc_from_crate_by_macro);
99
100        assert_ne!(loc_from_std_by_fn, loc_from_std_by_macro);
101        assert_ne!(loc_from_std_by_fn, loc_from_crate_by_macro);
102    }
103
104    struct Tuple {
105        from_std: Location,
106        from_crate: Location,
107    }
108
109    #[track_caller]
110    fn location_by_fn() -> Tuple {
111        let from_std = Location::from_std(std::panic::Location::caller());
112        let from_crate = Location::caller();
113
114        Tuple {
115            from_std,
116            from_crate,
117        }
118    }
119
120    #[track_caller]
121    fn location_by_macro() -> Tuple {
122        let from_std = Location {
123            file: file!(),
124            line: line!(),
125            column: column!(),
126        };
127
128        let from_crate = super::location!();
129
130        Tuple {
131            from_std,
132            from_crate,
133        }
134    }
135}