aven 0.1.1

Local-first task manager CLI and sync server
Documentation
use std::fs::{self, OpenOptions};
use std::path::{Path, PathBuf};

use anyhow::{Context, Result};
use tracing_subscriber::EnvFilter;

const APP_DIR: &str = "aven";
const LOG_FILE: &str = "aven.log";

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum LogMode {
    Cli,
    Tui,
    Daemon,
    Server,
}

pub(crate) fn init(_mode: LogMode) -> Result<()> {
    let filter = std::env::var("AVEN_LOG").unwrap_or_else(|_| "aven=info".to_string());
    let filter = EnvFilter::try_new(filter).context("invalid AVEN_LOG filter")?;
    let path = std::env::var_os("AVEN_LOG_FILE")
        .map(PathBuf::from)
        .unwrap_or(default_log_path()?);
    init_file(&path, filter)
}

fn default_log_path() -> Result<PathBuf> {
    let mut dir = std::env::var_os("XDG_STATE_HOME")
        .map(PathBuf::from)
        .filter(|path| path.is_absolute())
        .or_else(|| dirs::home_dir().map(|home| home.join(".local/state")))
        .context("could not find state directory")?;
    dir.push(APP_DIR);
    Ok(dir.join(LOG_FILE))
}

fn init_file(path: &Path, filter: EnvFilter) -> Result<()> {
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)
            .with_context(|| format!("create log directory {}", parent.display()))?;
    }
    let file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(path)
        .with_context(|| format!("open log file {}", path.display()))?;
    tracing_subscriber::fmt()
        .with_env_filter(filter)
        .with_ansi(false)
        .with_target(true)
        .compact()
        .with_writer(std::sync::Mutex::new(file))
        .try_init()
        .map_err(|err| anyhow::anyhow!("initialize tracing subscriber: {err}"))
}