docopticon 0.1.2

An argument-parser based on the obligatory help-text
Documentation
//! Trait-based XDG Base Directory-compliant saving and loading of files.
//!
//! ## Why does this need `std`.
//! Unfortunately, since there needs to be quite a lot of code to support OS file-handles,
//! file permissions and file attributes this module cannot be `no_std`. Main reason being
//! I do not see much reason in duplicating so much code from the standard library when in
//! all likelihood this part of the library only serves a use-case on systems where compiling
//! against Rust's standard library is completely fine.
//!
//! Supports:
//! * `System paths w/ fallbacks`
//! * `Home paths w/ fallbacks`
//! * `<application-name>.d/ - directory files`
use std::{
    convert::From,
    env,
    error::Error,
    fs,
    io::Error as IoError,
    path::{Path, PathBuf},
    str::Utf8Error,
    string::String,
};

use serde::{de, ser, Deserialize, Serialize};

mod cache;
pub use cache::CacheFile;
mod config;
pub use config::ConfigFile;
mod data;
pub use data::DataFile;
mod state;
pub use state::StateFile;

/// Load `$HOME` environment variable.
///
/// # Panic
/// Panics upon not being able to read `$HOME` or if `$HOME` specifies a relative instead of an
/// absolute directory (or is empty)
#[inline]
pub(crate) fn load_home_env() -> PathBuf {
    let mut path =
        PathBuf::from(env::var("HOME").expect("unable to read environment variable '$HOME'"));
    if path.is_relative() || path.as_os_str().is_empty() {
        panic!("'$HOME' is relative instead of absolute");
    }
    path
}

/// Assembles the full path for the file from the HOME_PATH_ENV, using HOME_PATH_FALLBACK as a
/// fallback.
///
/// # Panic
/// Panics upon not being able to read `$HOME`.
#[inline]
pub(crate) fn home_path(
    home_path_env: &str,
    fallback: &str,
    app_dir_name: &str,
    filename: &str,
) -> PathBuf {
    let mut path = if let Ok(path) = env::var(home_path_env) {
        if !path.starts_with("/") {
            let mut path = load_home_env();
            path.push(fallback);
            path
        } else {
            PathBuf::from(path)
        }
    } else {
        let mut path = load_home_env();
        path.push(fallback);
        path
    };
    path.push(app_dir_name);
    path.push(filename);
    path
}

/// Assembles all possible full paths for the file from the directories retrieved from
/// the SYSTEM_PATHS_ENV environment variable, using SYSTEM_PATHS_FALLBACK if it does not
/// exist.
///
/// If any of the paths are relative they are excluded.
#[inline]
pub(crate) fn system_paths(
    system_path_env: &str,
    fallback: &str,
    app_dir_name: &str,
    filename: &str,
) -> Vec<PathBuf> {
    let paths = if let Ok(paths) = env::var(system_path_env) {
        paths
    } else {
        fallback.to_string()
    };
    let mut paths: Vec<PathBuf> = paths
        .split(":")
        .filter_map(|path| {
            let path = PathBuf::from(path);
            if path.is_absolute() {
                Some(path)
            } else {
                None
            }
        })
        .collect();
    let mut iter = paths.iter_mut();
    while let Some(path) = iter.next() {
        path.push(app_dir_name);
        path.push(filename);
    }
    paths
}

/// Save to a given path with a given file name. Optionally creating all directories
/// needed on the way.
pub(crate) fn save_to_path<T: Serialize, E: Error + From<IoError>>(
    obj: &T,
    path: impl AsRef<Path>,
    create_dirs: bool,
) -> Result<(), E> {
    let path = path.as_ref();
    if let Some(parent) = path.parent() {
        if !path.is_dir() && create_dirs {
            fs::create_dir_all(parent)?;
        }
    }

    // let data = basic_toml::to_string(obj)?;
    // fs::write(path, data)?;

    Ok(())
}

/// Load from a given path.
pub(crate) fn load_from_path<'a, T: Deserialize<'a>, E: Error + From<IoError>>(
    path: impl AsRef<Path>,
) -> Result<T, E> {
    let path = path.as_ref();
    let data = fs::read_to_string(path)?;
    // let config = basic_toml::from_str(&data)?;

    todo!()
    // Ok(config)
}

/// Load all files in a path.
pub(crate) fn load_all_from_path<'a, T: Deserialize<'a>, E: Error + From<IoError>>(
    path: impl AsRef<Path>,
) -> Result<Vec<T>, E> {
    let path = path.as_ref();
    todo!()
}