1use std::path::PathBuf;
2
3use anyhow::{anyhow, Context, Result};
4use better_panic::Settings;
5use directories::ProjectDirs;
6use tracing::{error, level_filters::LevelFilter};
7use tracing_appender::{
8 non_blocking::WorkerGuard,
9 rolling::{RollingFileAppender, Rotation},
10};
11use tracing_subscriber::{
12 self, filter::EnvFilter, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer,
13};
14
15pub fn initialize_panic_handler() {
16 std::panic::set_hook(Box::new(|panic_info| {
17 if let Err(r) = crate::terminal::exit() {
18 error!("Unable to exit Terminal: {r:?}");
19 }
20
21 Settings::auto().most_recent_first(false).lineno_suffix(true).create_panic_handler()(panic_info);
22 std::process::exit(libc::EXIT_FAILURE);
23 }));
24}
25
26pub fn get_data_dir() -> Result<PathBuf> {
27 let directory = if let Ok(s) = std::env::var("SYSTEMCTL_TUI_DATA") {
28 PathBuf::from(s)
29 } else if let Some(proj_dirs) = ProjectDirs::from("com", "rgwood", "systemctl-tui") {
30 proj_dirs.data_local_dir().to_path_buf()
31 } else {
32 return Err(anyhow!("Unable to find data directory for systemctl-tui"));
33 };
34 Ok(directory)
35}
36
37pub fn get_config_dir() -> Result<PathBuf> {
38 let directory = if let Ok(s) = std::env::var("SYSTEMCTL_TUI_CONFIG") {
39 PathBuf::from(s)
40 } else if let Some(proj_dirs) = ProjectDirs::from("com", "rgwood", "systemctl-tui") {
41 proj_dirs.config_local_dir().to_path_buf()
42 } else {
43 return Err(anyhow!("Unable to find config directory for systemctl-tui"));
44 };
45 Ok(directory)
46}
47
48pub fn initialize_logging(enable_file_logging: bool) -> Result<Option<WorkerGuard>> {
49 let mut guard = None;
50
51 let file_layer = if enable_file_logging {
52 let directory = get_data_dir()?;
53 std::fs::create_dir_all(directory.clone()).context(format!("{directory:?} could not be created"))?;
54
55 let file_appender = RollingFileAppender::new(Rotation::DAILY, &directory, "systemctl-tui.log");
57
58 let (non_blocking, g) = tracing_appender::non_blocking(file_appender);
60
61 guard = Some(g);
63
64 tracing::info!(directory = %directory.display(), "Logging initialized");
66
67 Some(
69 tracing_subscriber::fmt::layer()
70 .with_writer(non_blocking)
71 .with_file(true)
72 .with_line_number(true)
73 .with_target(false)
74 .with_ansi(false)
75 .with_filter(EnvFilter::builder().with_default_directive(LevelFilter::INFO.into()).from_env_lossy()),
76 )
77 } else {
78 None
79 };
80
81 tui_logger::init_logger(tui_logger::LevelFilter::Debug)?;
82
83 let tui_layer = tui_logger::TuiTracingSubscriberLayer
84 .with_filter(EnvFilter::builder().with_default_directive(LevelFilter::INFO.into()).from_env_lossy());
85
86 tracing_subscriber::registry().with(file_layer).with(tui_layer).init();
87
88 Ok(guard)
89}
90
91#[macro_export]
97macro_rules! trace_dbg {
98 (target: $target:expr, level: $level:expr, $ex:expr) => {{
99 match $ex {
100 value => {
101 tracing::event!(target: $target, $level, ?value, stringify!($ex));
102 value
103 }
104 }
105 }};
106 (level: $level:expr, $ex:expr) => {
107 trace_dbg!(target: module_path!(), level: $level, $ex)
108 };
109 (target: $target:expr, $ex:expr) => {
110 trace_dbg!(target: $target, level: tracing::Level::DEBUG, $ex)
111 };
112 ($ex:expr) => {
113 trace_dbg!(level: tracing::Level::DEBUG, $ex)
114 };
115}
116
117pub fn version() -> String {
118 let author = clap::crate_authors!();
119
120 let version = env!("CARGO_PKG_VERSION");
121
122 let config_dir_path = get_config_dir().unwrap().display().to_string();
123 let data_dir_path = get_data_dir().unwrap().display().to_string();
124
125 format!(
126 "\
127{version}
128
129Authors: {author}
130
131Config directory: {config_dir_path}
132Data directory: {data_dir_path}"
133 )
134}