diff options
Diffstat (limited to 'template/src/base_dir.rs')
-rw-r--r-- | template/src/base_dir.rs | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/template/src/base_dir.rs b/template/src/base_dir.rs new file mode 100644 index 0000000..91e824a --- /dev/null +++ b/template/src/base_dir.rs @@ -0,0 +1,153 @@ +use std::{ + path::{Path, PathBuf}, + ops::{Deref, DerefMut}, + env, io +}; + +use serde::{ + ser::{Serialize, Serializer}, + de::{Deserialize, Deserializer} +}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct CwdBaseDir(PathBuf); + +impl CwdBaseDir { + + /// Creates a new `CwdBaseDir` instance containing exactly the given path. + pub fn new_unchanged(path: PathBuf) -> Self { + CwdBaseDir(path) + } + + /// Creates a `CwdBaseDir` from a path by prefixing the path with the + /// current working dir if it's relative. + /// + /// If the path is not relative it's directly used. + /// + /// # Os state side effects + /// + /// As this function accesses the current working directory (CWD) it's + /// not pure as the CWD can be changed (e.g. by `std::env::set_current_dir`). + /// + /// # Error + /// + /// As getting the CWD can fail this function can fail with a I/O Error, too. + pub fn from_path<P>(path: P) -> Result<Self, io::Error> + where P: AsRef<Path> + Into<PathBuf> + { + let path = + if path.as_ref().is_absolute() { + path.into() + } else { + let mut cwd = env::current_dir()?; + cwd.push(path.as_ref()); + cwd + }; + + Ok(CwdBaseDir(path)) + } + + /// Turns this path into a `PathBuf` by stripping the current working dir + /// if it starts with it. + /// + /// If this path does not start with the CWD it's returned directly. + /// + /// # Os state side effects + /// + /// As this function used the current working dir (CWD) it is affected + /// by any function changing the CWD as a side effect. + /// + /// # Error + /// + /// Accessing the current working dir can fail, as such this function + /// can fail. + pub fn to_base_path(&self) -> Result<&Path, io::Error> { + let cwd = env::current_dir()?; + self.strip_prefix(&cwd) + .or_else(|_err_does_not_has_that_prefix| { + Ok(&self) + }) + } + + /// Turns this instance into the `PathBuf` it dereferences to. + pub fn into_inner_with_prefix(self) -> PathBuf { + let CwdBaseDir(path) = self; + path + } +} + +impl Deref for CwdBaseDir { + type Target = PathBuf; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for CwdBaseDir { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AsRef<Path> for CwdBaseDir { + fn as_ref(&self) -> &Path { + &self.0 + } +} + + + +impl<'de> Deserialize<'de> for CwdBaseDir { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where D: Deserializer<'de> + { + use serde::de::Error; + let path_buf = PathBuf::deserialize(deserializer)?; + Self::from_path(path_buf) + .map_err(|err| D::Error::custom(err)) + } +} + +impl Serialize for CwdBaseDir { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where S: Serializer, + { + use serde::ser::Error; + let path = self.to_base_path() + .map_err(|err| S::Error::custom(err))?; + + path.serialize(serializer) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn from_path_does_not_affect_absolute_paths() { + let path = Path::new("/the/dog"); + let base_dir = CwdBaseDir::from_path(path).unwrap(); + assert_eq!(&*base_dir, Path::new("/the/dog")) + } + + #[test] + fn from_path_prefixes_with_cwd() { + let cwd = env::current_dir().unwrap(); + let expected = cwd.join("./the/dog"); + + let base_dir = CwdBaseDir::from_path("./the/dog").unwrap(); + assert_eq!(&*base_dir, &expected); + } + + #[test] + fn to_base_path_removes_cwd_prefix() { + let cwd = env::current_dir().unwrap(); + let dir = cwd.join("hy/there"); + let base_dir = CwdBaseDir::new_unchanged(dir); + let path = base_dir.to_base_path().unwrap(); + assert_eq!(path, Path::new("hy/there")); + } +}
\ No newline at end of file |