unifier-cli 0.2.0

Filesystem postbox for inter-process communication via a Unix tree
Documentation
//! Resolve the unifier state root (`~/.local/unifier` by default) and optional chroots.

use std::path::{Path, PathBuf};

use crate::constants::CHROOTS;
use crate::error::Result;
use crate::scope::validate_chroot_name;

const APP: &str = "unifier";

/// Effective store root for the current invocation (global root or a chroot subtree).
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnifierHome {
    global: PathBuf,
    effective: PathBuf,
    chroot: Option<String>,
}

impl UnifierHome {
    /// Uses `$UNIFIER_HOME`, else `~/.local/unifier`. With `chroot`, scopes to `chroots/<name>/`.
    pub fn resolve(explicit: Option<PathBuf>, chroot: Option<String>) -> Result<Self> {
        let global = resolve_global(explicit)?;
        let effective = match &chroot {
            Some(name) => {
                validate_chroot_name(name)?;
                global.join(CHROOTS).join(name)
            }
            None => global.clone(),
        };
        Ok(Self {
            global,
            effective,
            chroot,
        })
    }

    /// Directory used by put/get/send/poll and other data commands.
    pub fn path(&self) -> &Path {
        &self.effective
    }

    /// Top-level store (parent of `chroots/`).
    pub fn global_path(&self) -> &Path {
        &self.global
    }

    pub fn chroot_name(&self) -> Option<&str> {
        self.chroot.as_deref()
    }

    pub fn ensure(&self) -> Result<()> {
        std::fs::create_dir_all(self.path())?;
        Ok(())
    }
}

fn resolve_global(explicit: Option<PathBuf>) -> Result<PathBuf> {
    if let Some(p) = explicit {
        return Ok(p);
    }
    if let Ok(p) = std::env::var("UNIFIER_HOME") {
        return Ok(PathBuf::from(p));
    }
    let base = user_home()?.join(".local");
    Ok(base.join(APP))
}

fn user_home() -> Result<PathBuf> {
    std::env::var_os("HOME")
        .or_else(|| std::env::var_os("USERPROFILE"))
        .map(PathBuf::from)
        .ok_or_else(|| crate::Error::msg("could not resolve home directory"))
}