hyprscratch 0.6.4

Improved scratchpad functionality for Hyprland
use crate::{DEFAULT_LOGFILE, HYPRSCRATCH_DIR};
use chrono::Local;
use std::env::VarError;
use std::fs::{create_dir, File};
use std::io::{self, Write};
use std::path::Path;
use std::process::exit;
use std::sync::LockResult;

pub use LogLevel::*;
#[derive(PartialEq)]
pub enum LogLevel {
    Info,
    Warn,
    Error,
    Debug,
}

impl LogLevel {
    fn as_str<'a>(&self) -> &'a str {
        match self {
            Info => "INFO",
            Warn => "WARN",
            Error => "ERROR",
            Debug => "DEBUG",
        }
    }
}

pub trait LogErr<T> {
    fn unwrap_log(self, file: &str, line: u32) -> T;
    fn log_err(self, file: &str, line: u32);
}

macro_rules! impl_logerr {
    ($($t:ty),+) => {
        $(impl<T> LogErr<T> for $t {
            fn unwrap_log(self, file: &str, line: u32) -> T {
                match self {
                    Ok(t) => t,
                    Err(e) => {
                        let _ = log(format!("{e} at {file}:{line}"), Error);
                        exit(1)
                    }
                }
            }

            fn log_err(self, file: &str, line: u32) {
                if let Err(e) = self {
                    let _ = log(format!("{e} at {file}:{line}"), Warn);
                }
            }
        })+
    }
}

impl_logerr!(hyprland::Result<T>, io::Result<T>, notify::Result<T>,
    Result<T, VarError>, LockResult<T>);

impl<T> LogErr<T> for Option<T> {
    fn unwrap_log(self, file: &str, line: u32) -> T {
        if let Some(t) = self {
            t
        } else {
            let _ = log(format!("Function returned None at {file}:{line}"), Error);
            exit(1)
        }
    }

    fn log_err(self, file: &str, line: u32) {
        if self.is_none() {
            let _ = log(format!("Received None at {file}:{line}"), Warn);
        }
    }
}

fn get_log_file() -> io::Result<File> {
    let temp_dir = Path::new(HYPRSCRATCH_DIR);
    if !temp_dir.exists() {
        create_dir(temp_dir)?;
    }

    File::options()
        .create(true)
        .read(true)
        .append(true)
        .open(DEFAULT_LOGFILE)
}

fn write_msg(msg: &String, level: &LogLevel) -> io::Result<()> {
    get_log_file()?.write_all(
        format!(
            "{} {} {msg}\n",
            Local::now().format("%d.%m.%Y %H:%M:%S"),
            level.as_str()
        )
        .as_bytes(),
    )
}

fn exit_on_err(level: LogLevel) {
    if level == Error {
        if cfg!(debug_assertions) {
            panic!("Fatal");
        } else {
            exit(1);
        }
    }
}

pub fn log(msg: String, level: LogLevel) -> hyprland::Result<()> {
    write_msg(&msg, &level)?;
    println!("{msg}");
    exit_on_err(level);
    Ok(())
}