moon 0.1.22

universal linux-only tool.
use std::env;
use std::fs;
use std::path::PathBuf;

use crate::config::{Config, ico};
use crate::util::Res;

#[derive(Clone)]
pub struct Dirs {
    pub local: PathBuf,
    pub apps: PathBuf,
    pub manifests: PathBuf,
    pub tmp: PathBuf,
    pub bin: PathBuf,
    pub desktop: PathBuf,
}

impl Dirs {
    pub fn new() -> Res<Self> {
        let home = PathBuf::from(env::var_os("HOME").ok_or("HOME is not set")?);
        let data = env::var_os("XDG_DATA_HOME")
            .map(PathBuf::from)
            .filter(|p| p.is_absolute())
            .unwrap_or_else(|| home.join(".local/share"));
        let moon = data.join("moon");
        let local = home.join(".local");
        Ok(Dirs {
            bin: local.join("bin"),
            local,
            apps: moon.join("apps"),
            manifests: moon.join("manifests"),
            tmp: moon.join("tmp"),
            desktop: data.join("applications"),
        })
    }

    pub fn ensure(&self) -> Res<()> {
        for d in [&self.apps, &self.manifests, &self.tmp, &self.bin, &self.desktop] {
            fs::create_dir_all(d).map_err(|e| format!("cannot create {}: {e}", d.display()))?;
        }
        Ok(())
    }
}

pub struct Cleanup(pub PathBuf);

impl Drop for Cleanup {
    fn drop(&mut self) {
        let _ = fs::remove_dir_all(&self.0);
    }
}

pub fn warn_path(d: &Dirs) {
    let path = env::var("PATH").unwrap_or_default();
    let bin = d.bin.to_string_lossy().into_owned();
    if !path.split(':').any(|p| p == bin) {
        let cfg = Config::load();
        eprintln!(
            "warning: {} {bin} is not in your PATH. Add to your shell rc:\n    export PATH=\"{bin}:$PATH\"",
            cfg.icon(ico::WARN)
        );
    }
}