komichi 2.2.0

Application tools for working with file-system paths
Documentation
use crate::environ::EnvVal;
use crate::local::LocalInfo;
use camino::{Utf8Path, Utf8PathBuf};

pub struct Info {
    home: Utf8PathBuf,
    app_name: String,
    master: Utf8PathBuf,
    minion: Option<Utf8PathBuf>,
}

impl Info {
    pub fn new<T>(app_name: &str, home: &T) -> Self
    where
        T: AsRef<Utf8Path> + ?Sized,
    {
        let app_name = app_name.to_owned();
        let home = home.as_ref().to_owned();
        let master = home.join("Library").join("Application Support");
        let minion = Self::get_minion(&home, &master);
        Self {
            home,
            app_name,
            master,
            minion,
        }
    }

    fn get_minion(home: &Utf8Path, master: &Utf8Path) -> Option<Utf8PathBuf> {
        let minion = home.join("Library").join("ApplicationSupport");
        if !minion.exists() {
            return None;
        }
        if !minion.is_dir() {
            return None;
        }
        if !minion.is_symlink() {
            return None;
        }
        if let Some(source) = Self::get_link_source(&minion) {
            if source == master {
                return Some(minion);
            }
        }
        None
    }

    fn get_link_source(path: &Utf8Path) -> Option<Utf8PathBuf> {
        if !path.is_symlink() {
            return None;
        }
        if let Ok(source) = path.read_link_utf8() {
            if source.is_absolute() {
                return Some(source);
            }
            if let Some(parent) = path.parent() {
                let parent = parent.join(source);

                if let Ok(source) = parent.canonicalize_utf8() {
                    return Some(source);
                }
            }
        }
        None
    }

    fn is_proper(&self, path: &Utf8Path) -> bool {
        if let Some(path_source) = Self::get_link_source(path) {
            if path_source == *self.master {
                return true;
            }
            if let Some(minion) = self.minion.as_ref() {
                if *path_source == *minion {
                    return true;
                }
            }
            return false;
        }
        if *path == *self.master {
            return true;
        }

        if let Some(minion) = self.minion.as_ref() {
            if *path == *minion {
                return true;
            }
        }
        false
    }
}

impl LocalInfo for Info {
    fn get_cache_home(&self) -> Utf8PathBuf {
        let default = self.home.join("Library").join("Caches");
        let path: Utf8PathBuf = EnvVal::from("XDG_CACHE_HOME")
            .with_default_value(&default)
            .into();
        if path.exists() && path.is_dir() {
            if self.is_proper(&path) {
                return path.join(&self.app_name).join("cache");
            }
            return path.join(&self.app_name);
        }
        self.home.join(".cache").join(&self.app_name)
    }

    fn get_config_home(&self) -> Utf8PathBuf {
        let default = self.home.join("Library").join("Application Support");
        let path: Utf8PathBuf = EnvVal::from("XDG_CONFIG_HOME")
            .with_default_value(&default)
            .into();
        if path.exists() {
            if self.is_proper(&path) {
                return path.join(&self.app_name).join("config");
            }
            return path.join(&self.app_name);
        }
        self.home.join(".config").join(&self.app_name)
    }

    fn get_data_home(&self) -> Utf8PathBuf {
        let default = self.home.join("Library").join("Application Support");
        let path: Utf8PathBuf = EnvVal::from("XDG_DATA_HOME")
            .with_default_value(&default)
            .into();
        if path.exists() {
            if self.is_proper(&path) {
                return path.join(&self.app_name).join("data");
            }
            return path.join(&self.app_name);
        }
        self.home.join(".local").join("share").join(&self.app_name)
    }

    fn get_log_home(&self) -> Utf8PathBuf {
        let default = self.home.join("Library").join("Logs");
        let app_name = self.app_name.to_uppercase();
        let env_var_name = format!("{}_LOG_DIR", &app_name);
        let path: Utf8PathBuf = EnvVal::from(&env_var_name)
            .with_default_value(&default)
            .into();
        if path == default {
            return path.join(&self.app_name);
        }

        if self.is_proper(&path) {
            return path.join(&self.app_name).join("log");
        }
        self.home.join(".local").join("log").join(&self.app_name)
    }

    fn get_state_home(&self) -> Utf8PathBuf {
        let default = self.home.join("Library").join("Application Support");
        let path: Utf8PathBuf = EnvVal::from("XDG_STATE_HOME")
            .with_default_value(&default)
            .into();
        if path.exists() {
            if self.is_proper(&path) {
                return path.join(&self.app_name).join("state");
            }
            return path.join(&self.app_name);
        }
        self.home.join(".local").join("state").join(&self.app_name)
    }
}