charon_error/utils/
source_location.rs1use std::{fmt::Display, panic::Location};
8use tracing::Metadata;
9
10use crate::{ERGlobalSettings, GlobalSettings, ResultExt};
11
12#[derive(Debug, Clone, Default, Copy)]
17pub enum LinkDebugIde {
18 #[default]
20 NoLink,
21 File,
23 Vscode,
25}
26
27#[derive(Debug, Clone)]
32pub struct SourceLocation {
33 pub crate_name: &'static str,
35 pub crate_version: &'static str,
37 pub filename: String,
39 pub line_number: u32,
41 pub column_number: u32,
43}
44
45impl SourceLocation {
46 fn check_if_abs_path(path: &str) -> bool {
47 path.starts_with('/')
48 }
49
50 #[must_use]
52 #[track_caller]
53 pub fn from_caller_location() -> Self {
54 let caller = Location::caller();
55 Self {
56 crate_name: env!("CARGO_PKG_NAME"),
57 crate_version: env!("CARGO_PKG_VERSION"),
58 filename: caller.file().to_owned(),
59 line_number: caller.line(),
60 column_number: caller.column(),
61 }
62 }
63
64 #[must_use]
66 pub fn from_panic_location(location: &Location) -> Self {
67 Self {
68 crate_name: env!("CARGO_PKG_NAME"),
69 crate_version: env!("CARGO_PKG_VERSION"),
70 filename: location.file().to_owned(),
71 line_number: location.line(),
72 column_number: location.column(),
73 }
74 }
75
76 #[must_use]
78 pub fn from_metadata(metadata: &Metadata) -> Self {
79 Self {
80 crate_name: env!("CARGO_PKG_NAME"),
81 crate_version: env!("CARGO_PKG_VERSION"),
82 filename: metadata.file().unwrap_or("Unknown").to_owned(),
83 line_number: metadata.line().unwrap_or(0),
84 column_number: 0,
85 }
86 }
87
88 #[must_use]
90 pub fn display_location(&self, mut overwrite_link_fmt: Option<LinkDebugIde>) -> String {
91 if std::env::var("NO_COLOR").is_ok() {
93 overwrite_link_fmt = Some(LinkDebugIde::NoLink);
94 }
95
96 #[cfg(not(debug_assertions))]
98 if std::env::var("CARGO").is_err() {
99 overwrite_link_fmt = Some(LinkDebugIde::NoLink);
100 }
101
102 match overwrite_link_fmt
103 .or(Some(
104 ERGlobalSettings::get_global_settings()
105 .unwrap_error()
106 .link_format,
107 ))
108 .unwrap_or_default()
109 {
110 LinkDebugIde::NoLink => format!(
111 "{}:{}:{}",
112 self.filename, self.line_number, self.column_number
113 ),
114 LinkDebugIde::File => format!(
115 "\x1b]8;;file://{absolute_path}:{line}:{column}\x1b\\{relative_path}:{line}:{column}\x1b]8;;\x1b\\",
116 relative_path = Self::get_relative_path(&self.filename),
117 absolute_path = Self::get_absolute_path(&self.filename),
118 line = self.line_number,
119 column = self.column_number,
120 ),
121 LinkDebugIde::Vscode => format!(
122 "\x1b]8;;vscode://file/{absolute_path}:{line}:{column}\x1b\\{relative_path}:{line}:{column}\x1b]8;;\x1b\\",
123 relative_path = Self::get_relative_path(&self.filename),
124 absolute_path = Self::get_absolute_path(&self.filename),
125 line = self.line_number,
126 column = self.column_number,
127 ),
128 }
129 }
130
131 fn get_relative_path(path: &str) -> String {
132 let current_dir = std::env::current_dir().unwrap_error().display().to_string();
133
134 if Self::check_if_abs_path(path) {
135 let current_dir = format!("{current_dir}/");
137 if path.starts_with(¤t_dir) {
138 path.split_at(current_dir.len()).1.to_owned()
139 } else {
140 path.to_owned()
141 }
142 } else {
143 path.to_owned()
144 }
145 }
146
147 fn get_absolute_path(path: &str) -> String {
148 let current_dir = std::env::current_dir().unwrap_error().display().to_string();
149
150 if Self::check_if_abs_path(path) {
151 path.to_owned()
152 } else {
153 format!("{}/{}", current_dir, path)
154 }
155 }
156}
157
158impl Display for SourceLocation {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 write!(f, "{}", self.display_location(None))
161 }
162}