1use std::path::PathBuf;
2
3use color_eyre::eyre::Result;
4use directories::ProjectDirs;
5use lazy_static::lazy_static;
6use tracing::error;
7use tracing_error::ErrorLayer;
8use tracing_subscriber::{
9 self, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer,
10};
11
12lazy_static! {
13 pub static ref PROJECT_NAME: String = env!("CARGO_CRATE_NAME").to_uppercase().to_string();
14 pub static ref DATA_FOLDER: Option<PathBuf> =
15 std::env::var(format!("{}_DATA", PROJECT_NAME.clone()))
16 .ok()
17 .map(PathBuf::from);
18 pub static ref CONFIG_FOLDER: Option<PathBuf> =
19 std::env::var(format!("{}_CONFIG", PROJECT_NAME.clone()))
20 .ok()
21 .map(PathBuf::from);
22 pub static ref LOG_ENV: String = format!("{}_LOGLEVEL", PROJECT_NAME.clone());
23 pub static ref LOG_FILE: String = format!("{}.log", env!("CARGO_PKG_NAME"));
24}
25
26fn project_directory() -> Option<ProjectDirs> {
27 ProjectDirs::from("", "", &format!("{}-{}", env!("CARGO_PKG_NAME"), "editor"))
28}
29
30pub fn initialize_panic_handler() -> Result<()> {
31 let (panic_hook, eyre_hook) = color_eyre::config::HookBuilder::default()
32 .panic_section(format!(
33 "This is a bug. Consider reporting it at {}",
34 env!("CARGO_PKG_REPOSITORY")
35 ))
36 .capture_span_trace_by_default(false)
37 .display_location_section(false)
38 .display_env_section(false)
39 .into_hooks();
40 eyre_hook.install()?;
41 std::panic::set_hook(Box::new(move |panic_info| {
42 if let Ok(mut t) = crate::tui::Tui::new() {
43 if let Err(r) = t.exit() {
44 error!("Unable to exit Terminal: {:?}", r);
45 }
46 }
47
48 #[cfg(not(debug_assertions))]
49 {
50 use human_panic::{handle_dump, print_msg, Metadata};
51 let meta = Metadata {
52 version: env!("CARGO_PKG_VERSION").into(),
53 name: env!("CARGO_PKG_NAME").into(),
54 authors: env!("CARGO_PKG_AUTHORS").replace(':', ", ").into(),
55 homepage: env!("CARGO_PKG_HOMEPAGE").into(),
56 };
57
58 let file_path = handle_dump(&meta, panic_info);
59 print_msg(file_path, &meta)
61 .expect("human-panic: printing error message to console failed");
62 eprintln!("{}", panic_hook.panic_report(panic_info)); }
64 let msg = format!("{}", panic_hook.panic_report(panic_info));
65 log::error!("Error: {}", strip_ansi_escapes::strip_str(msg));
66
67 #[cfg(debug_assertions)]
68 {
69 better_panic::Settings::auto()
71 .most_recent_first(false)
72 .lineno_suffix(true)
73 .verbosity(better_panic::Verbosity::Full)
74 .create_panic_handler()(panic_info);
75 }
76
77 std::process::exit(libc::EXIT_FAILURE);
78 }));
79 Ok(())
80}
81
82pub fn get_data_dir() -> PathBuf {
83 let directory = if let Some(s) = DATA_FOLDER.clone() {
84 s
85 } else if let Some(proj_dirs) = project_directory() {
86 proj_dirs.data_local_dir().to_path_buf()
87 } else {
88 PathBuf::from(".").join(".data")
89 };
90 directory
91}
92
93pub fn get_config_dir() -> PathBuf {
94 let directory = if let Some(s) = CONFIG_FOLDER.clone() {
95 s
96 } else if let Some(proj_dirs) = project_directory() {
97 proj_dirs.config_local_dir().to_path_buf()
98 } else {
99 PathBuf::from(".").join(".config")
100 };
101 directory
102}
103
104pub fn initialize_logging() -> Result<()> {
105 let directory = get_data_dir();
106 std::fs::create_dir_all(directory.clone())?;
107 let log_path = directory.join(LOG_FILE.clone());
108 let log_file = std::fs::File::create(log_path)?;
109 std::env::set_var(
110 "RUST_LOG",
111 std::env::var("RUST_LOG")
112 .or_else(|_| std::env::var(LOG_ENV.clone()))
113 .unwrap_or_else(|_| format!("{}=info", env!("CARGO_CRATE_NAME"))),
114 );
115 let file_subscriber = tracing_subscriber::fmt::layer()
116 .with_file(true)
117 .with_line_number(true)
118 .with_writer(log_file)
119 .with_target(false)
120 .with_ansi(false)
121 .with_filter(tracing_subscriber::filter::EnvFilter::from_default_env());
122 tracing_subscriber::registry()
123 .with(file_subscriber)
124 .with(ErrorLayer::default())
125 .init();
126 Ok(())
127}
128
129#[macro_export]
135macro_rules! trace_dbg {
136 (target: $target:expr, level: $level:expr, $ex:expr) => {{
137 match $ex {
138 value => {
139 tracing::event!(target: $target, $level, ?value, stringify!($ex));
140 value
141 }
142 }
143 }};
144 (level: $level:expr, $ex:expr) => {
145 trace_dbg!(target: module_path!(), level: $level, $ex)
146 };
147 (target: $target:expr, $ex:expr) => {
148 trace_dbg!(target: $target, level: tracing::Level::DEBUG, $ex)
149 };
150 ($ex:expr) => {
151 trace_dbg!(level: tracing::Level::DEBUG, $ex)
152 };
153}
154
155pub fn version() -> String {
156 let author = clap::crate_authors!();
157
158 let config_dir_path = get_config_dir().display().to_string();
160 let data_dir_path = get_data_dir().display().to_string();
161
162 format!(
163 "\
164Authors: {author}
165
166Config directory: {config_dir_path}
167Data directory: {data_dir_path}"
168 )
169}