1use std::{env, path::PathBuf, sync::LazyLock};
2
3use directories::ProjectDirs;
4use tracing_error::ErrorLayer;
5use tracing_subscriber::{
6 EnvFilter, Layer,
7 fmt::{self},
8 layer::SubscriberExt,
9 util::SubscriberInitExt,
10};
11
12use crate::{app::cli::LogLevel, errors::AppError};
13
14pub static PROJECT_NAME: LazyLock<String> =
15 LazyLock::new(|| env!("CARGO_CRATE_NAME").to_uppercase().to_string());
16pub static DATA_FOLDER: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
17 env::var(format!("{}_DATA", &*PROJECT_NAME))
18 .ok()
19 .map(PathBuf::from)
20});
21
22pub static LOG_ENV: LazyLock<String> = LazyLock::new(|| format!("{}_LOG_LEVEL", &*PROJECT_NAME));
23pub static LOG_FILE: LazyLock<String> = LazyLock::new(|| format!("{}.log", env!("CARGO_PKG_NAME")));
24
25pub fn get_data_dir() -> PathBuf {
26 if let Some(s) = DATA_FOLDER.clone() {
27 s
28 } else if let Some(proj_dirs) = project_directory() {
29 proj_dirs.data_local_dir().to_path_buf()
30 } else {
31 PathBuf::from(".").join(".data")
32 }
33}
34
35pub(crate) fn project_directory() -> Option<ProjectDirs> {
36 ProjectDirs::from("com", "jayanaxhf", env!("CARGO_PKG_NAME"))
37}
38
39pub fn init(cfg: LoggingConfig) -> Result<(), AppError> {
40 let directory = get_data_dir();
42 std::fs::create_dir_all(directory.clone())?;
43 let log_path = directory.join(&*LOG_FILE);
44 let log_file = std::fs::File::create(log_path)?;
45
46 let env_filter = EnvFilter::builder()
47 .with_default_directive(cfg.level.try_into().map_err(anyhow::Error::from)?);
48 let env_filter = env_filter
52 .try_from_env()
53 .or_else(|_| env_filter.with_env_var(LOG_ENV.clone()).from_env())
54 .map_err(Into::<anyhow::Error>::into)?;
55 let file_subscriber = fmt::layer()
56 .with_file(true)
57 .with_line_number(true)
58 .with_writer(log_file)
59 .with_ansi(false)
60 .with_target(true)
61 .with_filter(env_filter);
62 tracing_subscriber::registry()
63 .with(file_subscriber)
64 .with(ErrorLayer::default())
65 .try_init()?;
66
67 Ok(())
68}
69
70#[derive(Clone, Debug)]
71pub struct LoggingConfig {
72 level: LogLevel,
73}
74
75impl LoggingConfig {
76 pub fn new(level: LogLevel) -> Self {
77 Self { level }
78 }
79}