summaryrefslogtreecommitdiffstats
path: root/template/src/base_dir.rs
diff options
context:
space:
mode:
Diffstat (limited to 'template/src/base_dir.rs')
-rw-r--r--template/src/base_dir.rs153
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