pub mod ext;
pub mod macros;
pub mod util;


pub use self::{ext::*, macros::*};


use std::borrow::Cow;
use std::ffi::OsStr;
use std::path::{Component, Path, PathBuf, Prefix};


// returns the volume name for some path, intended for display.
pub fn volume_name(path: &Path) -> Option<String> {
    fn display(string: &OsStr) -> Cow<str> {
        string.to_string_lossy()
    }


    if let Some(component) = path.components().next() {
        let volume = match component {
            Component::Prefix(prefix) => match prefix.kind() {
                Prefix::Verbatim(name) => format!("{}", display(name)),
                Prefix::VerbatimUNC(server, share) => format!("{}\\{}", display(server), display(share)),
                Prefix::VerbatimDisk(disk) => format!("{}:\\", disk as char),
                Prefix::DeviceNS(namespace) => format!("{}", display(namespace)),
                Prefix::UNC(server, share) => format!("{}\\{}\\", display(server), display(share)),
                Prefix::Disk(disk) => format!("{}:\\", disk as char),
            },
            Component::RootDir => "/".to_owned(),
            Component::CurDir => ".".to_owned(),
            Component::ParentDir => "..".to_owned(),
            Component::Normal(x) => x.to_string_lossy().into(),
        };

        Some(volume)
    } else {
        None
    }
}



pub trait PathExt {
    /// appends a `path` to self. if `path` is a unix-like rooted path, then it is transformed into a relative path.
    ///
    /// # examples
    ///
    /// ```
    /// # use std::path::PathBuf;
    /// # use hina::path::PathExt;
    ///
    /// let mut path = PathBuf::from("/home/code");
    /// path.append("/rust/projects/awesome");
    ///
    /// let expected = PathBuf::from("/home/code/rust/projects/awesome");
    /// assert_eq!(path, expected);
    /// ```
    fn append(&mut self, path: impl AsRef<Path>);
}

impl PathExt for PathBuf {
    fn append(&mut self, path: impl AsRef<Path>) {
        crate::path::util::__for_internal_usage_only_append__(self, path);
    }
}



pub struct PathBufEx;

impl PathBufEx {
    /// constructs a `PathBuf` from a slice of path components
    ///
    /// # examples
    ///
    /// ```
    /// use hina::path::PathBufEx;
    /// use std::path::PathBuf;
    ///
    /// let path = PathBufEx::from(&["/home", "code", "/projects"]);
    /// let expected = PathBuf::from("home/code/projects");
    /// assert_eq!(path, expected);
    /// ```
    pub fn from(args: &[impl AsRef<Path>]) -> PathBuf {
        let mut path = PathBuf::new();

        for arg in args {
            path.append(arg);
        }

        path
    }
}



pub struct PathEx;

impl PathEx {
    /// constructs a `Path` from a slice of path components
    ///
    /// # examples
    ///
    /// ```
    /// use hina::path::PathEx;
    /// use std::path::Path;
    ///
    /// let path = PathEx::from(&["/home", "code", "/projects"]);
    /// let expected = Path::new("home/code/projects");
    /// assert_eq!(path, expected);
    /// ```
    pub fn from(args: &[impl AsRef<Path>]) -> PathBuf {
        PathBufEx::from(args)
    }
}