nedots 0.1.3

A tool to manage configuration files/(ne)dots.
use crate::errors::Error;
use anyhow::Context;
use std::path::{Path, PathBuf};

pub(crate) trait ResolvePath {
    fn resolve_path(&self) -> anyhow::Result<PathBuf>;
}

impl ResolvePath for Path {
    fn resolve_path(&self) -> anyhow::Result<PathBuf> {
        self.to_path_buf().resolve_path()
    }
}

impl ResolvePath for PathBuf {
    fn resolve_path(&self) -> anyhow::Result<PathBuf> {
        if !&self.exists() {
            let path = Path::new(env!("HOME")).join(self);
            return path
                .canonicalize()
                .with_context(|| format!("Failed to canonicalize {}", path.display()));
        }

        Ok(self.to_path_buf())
    }
}

pub(crate) trait StripPrefix {
    fn strip_prefix(&self, prefix: &str) -> anyhow::Result<PathBuf>;
}

impl StripPrefix for PathBuf {
    fn strip_prefix(&self, prefix: &str) -> anyhow::Result<PathBuf> {
        match Path::strip_prefix(self, prefix) {
            Ok(path) => Ok(path.to_path_buf()),
            Err(err) => {
                let err = Error::StripPrefix {
                    prefix: String::from("/"),
                    path: self.display().to_string(),
                    err,
                };
                Err(err.into())
            }
        }
    }
}

impl StripPrefix for Path {
    fn strip_prefix(&self, prefix: &str) -> anyhow::Result<PathBuf> {
        self.to_path_buf().strip_prefix(prefix)
    }
}

pub(crate) trait MakeDirs {
    fn make_all_dirs(&self) -> anyhow::Result<()>;
}

impl MakeDirs for Path {
    fn make_all_dirs(&self) -> anyhow::Result<()> {
        match std::fs::create_dir_all(self) {
            Ok(_) => Ok(()),
            Err(err) => {
                let err = Error::MakeDir {
                    path: self.display().to_string(),
                    err,
                };
                Err(err.into())
            }
        }
    }
}

impl MakeDirs for PathBuf {
    fn make_all_dirs(&self) -> anyhow::Result<()> {
        self.as_path().make_all_dirs()
    }
}

pub(crate) trait RemoveDirs {
    fn remove_all_dirs(&self) -> anyhow::Result<()>;
}

impl RemoveDirs for Path {
    fn remove_all_dirs(&self) -> anyhow::Result<()> {
        match std::fs::remove_dir_all(self) {
            Ok(_) => Ok(()),
            Err(err) => {
                let err = Error::RemoveDir {
                    path: self.display().to_string(),
                    err,
                };
                Err(err.into())
            }
        }
    }
}

impl RemoveDirs for PathBuf {
    fn remove_all_dirs(&self) -> anyhow::Result<()> {
        self.as_path().remove_all_dirs()
    }
}

pub(crate) trait Metadata {
    fn get_metadata(&self) -> anyhow::Result<std::fs::Metadata>;
    fn get_modified(&self) -> anyhow::Result<std::time::SystemTime>;
}

impl Metadata for Path {
    fn get_metadata(&self) -> anyhow::Result<std::fs::Metadata> {
        match self.metadata() {
            Ok(metadata) => Ok(metadata),
            Err(_) => {
                let err = Error::Metadata(self.display().to_string());
                Err(err.into())
            }
        }
    }

    fn get_modified(&self) -> anyhow::Result<std::time::SystemTime> {
        match self.get_metadata()?.modified() {
            Ok(modified) => Ok(modified),
            Err(_) => {
                let err = Error::ModifiedTime(self.display().to_string());
                Err(err.into())
            }
        }
    }
}

impl Metadata for PathBuf {
    fn get_metadata(&self) -> anyhow::Result<std::fs::Metadata> {
        self.as_path().get_metadata()
    }

    fn get_modified(&self) -> anyhow::Result<std::time::SystemTime> {
        self.as_path().get_modified()
    }
}

#[cfg(test)]
mod tests {}