Five years have passed, time for updates
[symlink] / src / lib.rs
index 790cb68a20ad4137f5f5db6f36cc820fbb5c0231..6019c1fcc7dfae6c76a692901c26d4298d871136 100644 (file)
@@ -1,9 +1,12 @@
 //! A small, cross-platform crate for creating symlinks.
 //!
-#![cfg_attr(not(any(target_os = "redox", unix, windows)), doc = "**This platform is not Unix, Windows or Redox; symlinks are not available.**")]
-//!
-//! For efficiency, you should prefer to use `symlink_file` or `symlink_dir`—whichever is
-//! appropriate—rather than `symlink_auto`
+//! For efficiency, you should prefer to use [`symlink_file`] or [`symlink_dir`]—whichever is
+//! appropriate—rather than [`symlink_auto`].
+
+// Building docs produces rustdoc::broken_intra_doc_links warnings on std::os::{windows, unix},
+// depending on your platform. This is unfortunate because I then can’t RUSTDOCFLAGS="-D warnings"
+// unless I suppress those, but suppressing those ones alone is messy, and suppressing all harmful,
+// so I’m just leaving it at spurious square brackets being left in the output.
 
 // It’s generally nicer to produce an empty crate on unsupported platforms than to explode.
 
@@ -12,65 +15,34 @@ use std::io;
 use std::path::Path;
 
 #[cfg(windows)]
-mod internal {
-    pub use std::os::windows::fs::{symlink_file, symlink_dir};
-    pub use std::fs::remove_dir as remove_symlink_dir;
-    use std::fs;
-    use std::io;
-    use std::mem;
-    use std::path::Path;
-
-    #[inline]
-    pub fn symlink_auto<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
-        if fs::metadata(src.as_ref())?.is_dir() {
-            symlink_dir(src.as_ref(), dst.as_ref())
-        } else {
-            symlink_file(src.as_ref(), dst.as_ref())
-        }
-    }
-
-    // Copied from the Rust standard library, src/libstd/sys/windows/fs.rs, because it’s an
-    // implementation detail that isn’t currently exposed in the public interface; I decided to do
-    // it this way rather than depending on the Debug implementation (which likewise could change).
-    #[derive(PartialEq)]
-    enum FileType {
-        Dir, File, SymlinkFile, SymlinkDir, ReparsePoint, MountPoint,
-    }
-
-    impl FileType {
-        pub fn is_symlink_dir(&self) -> bool {
-            *self == FileType::SymlinkDir || *self == FileType::MountPoint
-        }
-    }
+#[path = "windows/mod.rs"]
+mod internal;
 
-    #[inline]
-    pub fn remove_symlink_auto<P: AsRef<Path>>(path: P) -> io::Result<()> {
-        // We need to know whether it’s wrapping a SymlinkFile or SymlinkDir, but in the interests
-        // of consistency this crucial information is concealed. This is why unsafe transmutation
-        // is necessary: to determine whether it’s a symlink file or a symlink dir.
-        let file_type = fs::metadata(path.as_ref())?.file_type();
-        let fs_imp_file_type = unsafe { mem::transmute::<fs::FileType, FileType>(file_type) };
-        if fs_imp_file_type.is_symlink_dir() {
-            fs::remove_dir(path)
-        } else if file_type.is_symlink() {
-            fs::remove_file(path)
-        } else {
-            Err(io::Error::new(io::ErrorKind::InvalidInput, "path is not a symlink"))
-        }
-    }
-}
-
-#[cfg(any(target_os = "redox", unix))]
+#[cfg(not(windows))]
 mod internal {
     pub use std::fs::remove_file as remove_symlink_dir;
     pub use std::fs::remove_file as remove_symlink_auto;
-    // Note that this symlink function takes src and dst as &Path rather than as impl AsRef<Path>.
-    // I don’t know why that is, but I think we’ll go with impl AsRef<Path> in our public
-    // functions. Because of this disparity of signature, when I say that things are equivalent to
-    // calling std::os::unix::fs::symlink on Unix, you can see that I’m not being *quite* rigorous.
+    // Look, frankly, std::fs::soft_link and std::os::unix::fs::symlink call the same function,
+    // so this probably whole separate mod probably isn’t even warranted.
+    // But deprecated blah blah blah so I decided to use the std::os one anyway.
+    #[cfg(unix)]
     pub use std::os::unix::fs::{symlink as symlink_auto,
                                 symlink as symlink_file,
                                 symlink as symlink_dir};
+    #[cfg(not(unix))]
+    // The compiler claims that std::fs::soft_link has been “replaced with
+    // std::os::unix::fs::symlink and std::os::windows::fs::{symlink_file, symlink_dir}”
+    // (rustc nightly 2021-12-26 deprecation warning message), but although that was true enough
+    // when it was deprecated, it’s no longer quite true because of the wasm32-wasi target, which
+    // supports symlinks through std::fs::soft_link but has no stable alternative (as I write,
+    // std::os::wasi::fs::symlink_path is behind feature(wasi_ext)). Frankly, I think that’s a fair
+    // (though imperfect) reason to *undeprecate* soft_link. Who knows what other platforms may in
+    // the future stop returning std::io::ErrorKind::Unsupported errors and start supporting
+    // std::fs::soft_link? (And for clarity, I note that no others do at the time of writing.)
+    #[allow(deprecated)]
+    pub use std::fs::{soft_link as symlink_auto,
+                      soft_link as symlink_file,
+                      soft_link as symlink_dir};
 }
 
 /// Create a symlink (non-preferred way).
@@ -80,21 +52,19 @@ mod internal {
 /// type of symlink based on that result. Therefore, if the destination does not exist or if you do
 /// not have permission to fetch its metadata, this will return an error on Windows.
 ///
-/// On Unix platforms there is no distinction, so this isn’t magic: it’s precisely equivalent to
-/// calling `std::os::unix::fs::symlink`.
+/// On other platforms there is no distinction, so this isn’t magic: it’s precisely equivalent to
+/// calling [`std::os::unix::fs::symlink`] or [`std::fs::soft_link`].
 ///
 /// # A note on using this function
 ///
 /// Because this is slightly less efficient and more hazardous on Windows, you should prefer to use
-/// [`symlink_file`](fn.symlink_file.html) or [`symlink_dir`](fn.symlink_dir.html) instead. Only
-/// use this if you don’t know or care whether the destination is a file or a directory (but even
-/// then, you do need to know that it exists).
+/// [`symlink_file`] or [`symlink_dir`] instead. Only use this if you don’t know or care whether
+/// the destination is a file or a directory (but even then, you do need to know that it exists).
 ///
 /// # Errors
 ///
 /// An error will be returned if the symlink cannot be created, or—on Windows—if the destination
 /// does not exist or cannot be read.
-#[cfg(any(target_os = "redox", unix, windows))]
 #[inline]
 pub fn symlink_auto<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
     internal::symlink_auto(src.as_ref(), dst.as_ref())
@@ -102,17 +72,19 @@ pub fn symlink_auto<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Resul
 
 /// Create a symlink to a file.
 ///
-/// On Windows, this is equivalent to `std::os::windows::fs::symlink_file`. If you call it with a
-/// directory as the destination, TODO CONSEQUENCES.
+/// On Windows, this is equivalent to [`std::os::windows::fs::symlink_file`]. If you call it with a
+/// directory as the destination, [something may happen; you never know what][fow].
 ///
-/// On Unix, this is equivalent to `std::os::unix::fs::symlink`. If you call it with a directory as
-/// the destination, nothing bad will happen, but you’re ruining your cross-platform technique and
-/// ruining the point of this crate, so please don’t.
+/// On Unix, this is equivalent to [`std::os::unix::fs::symlink`], and on other platforms it’s
+/// equivalent to [`std::fs::soft_link`]. If you call it with a directory as the destination,
+/// nothing bad will happen, but you’re ruining your cross-platform technique and ruining the point
+/// of this crate, so please don’t.
 ///
 /// # Errors
 ///
 /// An error will be returned if the symlink cannot be created.
-#[cfg(any(target_os = "redox", unix, windows))]
+///
+/// [fow]: https://en.wikipedia.org/wiki/A_Fish_Out_of_Water_(book)
 #[inline]
 pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
     internal::symlink_file(src.as_ref(), dst.as_ref())
@@ -120,17 +92,19 @@ pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Resul
 
 /// Create a symlink to a directory.
 ///
-/// On Windows, this is equivalent to `std::os::windows::fs::symlink_dir`. If you call it with a
-/// directory as the destination, TODO CONSEQUENCES.
+/// On Windows, this is equivalent to [`std::os::windows::fs::symlink_dir`]. If you call it with a
+/// directory as the destination, [something may happen; you never know what][fow].
 ///
-/// On Unix, this is equivalent to `std::os::unix::fs::symlink`. If you call it with a directory as
-/// the destination, nothing bad will happen, but you’re ruining your cross-platform technique and
-/// ruining the point of this crate, so please don’t.
+/// On Unix, this is equivalent to [`std::os::unix::fs::symlink`], and on other platforms it’s
+/// equivalent to [`std::fs::soft_link`]. If you call it with a directory as the destination,
+/// nothing bad will happen, but you’re ruining your cross-platform technique and ruining the point
+/// of this crate, so please don’t.
 ///
 /// # Errors
 ///
 /// An error will be returned if the symlink cannot be created.
-#[cfg(any(target_os = "redox", unix, windows))]
+///
+/// [fow]: https://en.wikipedia.org/wiki/A_Fish_Out_of_Water_(book)
 #[inline]
 pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
     internal::symlink_dir(src.as_ref(), dst.as_ref())
@@ -144,15 +118,13 @@ pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result
 /// # A note on using this function
 ///
 /// Because this is slightly less efficient on Windows, you should prefer to use
-/// [`remove_symlink_file`](fn.remove_symlink_file.html) or
-/// [`remove_symlink_dir`](fn.remove_symlink_dir.html) instead. Only use this if you don’t know or
+/// [`remove_symlink_file`] or [`remove_symlink_dir`] instead. Only use this if you don’t know or
 /// care whether the destination is a file or a directory (but even then, you do need to know that
 /// it exists).
 ///
 /// # Errors
 ///
 /// An error will be returned if the symlink cannot be removed.
-#[cfg(any(target_os = "redox", unix, windows))]
 #[inline]
 pub fn remove_symlink_auto<P: AsRef<Path>>(path: P) -> io::Result<()> {
     internal::remove_symlink_auto(path)
@@ -160,10 +132,9 @@ pub fn remove_symlink_auto<P: AsRef<Path>>(path: P) -> io::Result<()> {
 
 /// Remove a directory symlink.
 ///
-/// On Windows, this corresponds to `std::fs::remove_dir`.
+/// On Windows, this corresponds to [`std::fs::remove_dir`].
 ///
-/// On Unix, this corresponds to `std::fs::remove_file`.
-#[cfg(any(target_os = "redox", unix, windows))]
+/// On Unix, this corresponds to [`std::fs::remove_file`].
 #[inline]
 pub fn remove_symlink_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
     internal::remove_symlink_dir(path)
@@ -171,11 +142,8 @@ pub fn remove_symlink_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
 
 /// Remove a file symlink.
 ///
-/// This just calls `std::fs::remove_file`, but the function is provided here to correspond to
-/// `remove_symlink_dir`.
-///
-/// On Unix, this corresponds to `std::fs::remove_file`.
-#[cfg(any(target_os = "redox", unix, windows))]
+/// This just calls [`std::fs::remove_file`], but the function is provided here to correspond to
+/// [`remove_symlink_dir`].
 #[inline]
 pub fn remove_symlink_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
     fs::remove_file(path)