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#[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}