Repository: vitiral/path_abs (language: rust)

Issue:
put join fn in PathOps
See this discussion:

https://github.com/vitiral/path_abs/pull/40#discussion_r


Discussion / hints:

Fix patch:
diff --git a/src/abs.rs b/src/abs.rs
index b6bc24e..c3dabac 100644
--- a/src/abs.rs
+++ b/src/abs.rs
@@ -204,6 +204,11 @@ impl PathOps for PathAbs {
         Ok(PathAbs(self.0.concat(path)?))
     }
 
+    fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
+        let buf = Path::join(self.as_path(), path);
+        Self::Output::new_unchecked(buf)
+    }
+
     fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
         PathAbs(self.0.with_file_name(file_name))
     }
diff --git a/src/dir.rs b/src/dir.rs
index 2b9405f..34182d8 100644
--- a/src/dir.rs
+++ b/src/dir.rs
@@ -509,6 +509,11 @@ impl PathOps for PathDir {
         Ok(self.0.concat(path)?)
     }
 
+    fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
+        let buf = Path::join(self.as_path(), path);
+        Self::Output::new_unchecked(buf)
+    }
+
     fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
         self.0.with_file_name(file_name)
     }
diff --git a/src/file.rs b/src/file.rs
index 34f2bfe..b712350 100644
--- a/src/file.rs
+++ b/src/file.rs
@@ -556,6 +556,11 @@ impl PathOps for PathFile {
         Ok(self.0.concat(path)?)
     }
 
+    fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
+        let buf = Path::join(self.as_path(), path);
+        Self::Output::new_unchecked(buf)
+    }
+
     fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
         self.0.with_file_name(file_name)
     }
diff --git a/src/lib.rs b/src/lib.rs
index 27c85f3..fb2fc2c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -244,9 +244,9 @@ mod write;
 pub use crate::abs::PathAbs;
 pub use crate::dir::{ListDir, PathDir};
 pub use crate::file::PathFile;
-pub use crate::ty::PathType;
 #[cfg(feature = "serialize")]
 pub use crate::ser::PathSer;
+pub use crate::ty::PathType;
 
 pub use crate::edit::FileEdit;
 pub use crate::read::FileRead;
@@ -400,13 +400,12 @@ pub trait PathInfo {
     }
 
     fn strip_prefix<P>(&self, base: P) -> std::result::Result<&Path, path::StripPrefixError>
-        where
-            P: AsRef<Path>
+    where
+        P: AsRef<Path>,
     {
         Path::strip_prefix(self.as_path(), base)
     }
 
-
     fn starts_with<P: AsRef<Path>>(&self, base: P) -> bool {
         Path::starts_with(self.as_path(), base)
     }
@@ -423,10 +422,6 @@ pub trait PathInfo {
         Path::extension(self.as_path())
     }
 
-    fn join<P: AsRef<Path>>(&self, path: P) -> PathBuf {
-        Path::join(self.as_path(), path)
-    }
-
     fn components(&self) -> Components<'_> {
         Path::components(self.as_path())
     }
@@ -846,6 +841,14 @@ pub trait PathOps: PathInfo {
     /// [`PathBuf::join`]: https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.join
     fn concat<P: AsRef<Path>>(&self, path: P) -> Result<Self::Output>;
 
+    /// An exact replica of `std::path::Path::join` with all of its gotchas and pitfalls,, except
+    /// returns a more relevant type.
+    ///
+    /// In general, prefer [`concat`]
+    ///
+    /// [`concat`]: trait.PathOps.html#method.concat
+    fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output;
+
     /// Creates a new path object like `self` but with the given file name.
     ///
     /// The same as [`std::path::Path::with_file_name()`], except that the
@@ -898,6 +901,10 @@ impl PathOps for Path {
         Ok(res)
     }
 
+    fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
+        Path::join(self, path)
+    }
+
     fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
         let mut res = self.to_owned();
         res.set_file_name(file_name);
@@ -918,6 +925,10 @@ impl PathOps for PathBuf {
         self.as_path().concat(path)
     }
 
+    fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
+        Path::join(self, path)
+    }
+
     fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
         self.as_path().with_file_name(file_name)
     }
@@ -936,6 +947,11 @@ impl PathOps for Arc<PathBuf> {
         Ok(res)
     }
 
+    fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
+        let buf = Path::join(self, path);
+        Arc::new(buf)
+    }
+
     fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
         let mut res = self.clone();
         Arc::make_mut(&mut res).set_file_name(file_name);
diff --git a/src/ser.rs b/src/ser.rs
index 51532e4..345063d 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -6,12 +6,12 @@
  * copied, modified, or distributed except according to those terms.
  */
 use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
+use std::fmt;
 use std::string::ToString;
 use std_prelude::*;
-use std::fmt;
 use stfu8;
 
-use super::{PathOps, PathMut};
+use super::{PathMut, PathOps};
 
 use std::ffi::{OsStr, OsString};
 #[cfg(unix)]
@@ -28,6 +28,10 @@ impl PathSer {
     pub fn new<P: Into<Arc<PathBuf>>>(path: P) -> Self {
         PathSer(path.into())
     }
+
+    pub fn as_path(&self) -> &Path {
+        self.as_ref()
+    }
 }
 
 impl fmt::Debug for PathSer {
@@ -61,6 +65,11 @@ impl PathOps for PathSer {
         Ok(PathSer(self.0.concat(path)?))
     }
 
+    fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
+        let buf = Path::join(self.as_path(), path);
+        Self::Output::new(buf)
+    }
+
     fn with_file_name<S: AsRef<OsStr>>(&self, file_name: S) -> Self::Output {
         PathSer(self.0.with_file_name(file_name))
     }
@@ -118,7 +127,6 @@ impl<P: AsRef<str>> From<P> for PathSer {
     }
 }
 
-
 impl From<PathSer> for Arc<PathBuf> {
     fn from(path: PathSer) -> Arc<PathBuf> {
         path.0
@@ -241,7 +249,7 @@ impl<'de> Deserialize<'de> for PathDir {
 
 #[cfg(test)]
 mod tests {
-    use super::super::{PathDir, PathFile, PathOps, PathType, PathInfo, PathMut};
+    use super::super::{PathDir, PathFile, PathInfo, PathMut, PathOps, PathType};
     use super::*;
 
     #[cfg(unix)]
@@ -297,7 +305,10 @@ mod tests {
     /// Just test that it has all the methods.
     fn sanity_ser() {
         let mut path = PathSer::from("example/path");
-        assert_eq!(path.join("joined"), Path::new("example/path/joined"));
+        assert_eq!(
+            path.join("joined").as_path(),
+            Path::new("example/path/joined")
+        );
         assert_eq!(path.is_absolute(), false);
 
         path.append("appended").unwrap();
@@ -305,6 +316,9 @@ mod tests {
         path.pop_up().unwrap();
         assert_eq!(path.as_path(), Path::new("example/path"));
 
-        assert_eq!(path.concat("/concated").unwrap().as_path(), Path::new("example/path/concated"));
+        assert_eq!(
+            path.concat("/concated").unwrap().as_path(),
+            Path::new("example/path/concated")
+        );
     }
 }
diff --git a/src/ty.rs b/src/ty.rs
index 9977829..2ab343c 100644
--- a/src/ty.rs
+++ b/src/ty.rs
@@ -217,6 +217,11 @@ impl PathOps for PathType {
         }
     }
 
+    fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
+        let buf = Path::join(self.as_path(), path);
+        Self::Output::new_unchecked(buf)
+    }
+
     fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
         match self {
             PathType::File(p) => p.with_file_name(file_name),


Test patch: