X-Git-Url: https://git.chrismorgan.info/symlink/blobdiff_plain/d96e3f836ec22848d1f3be1dfc00305bb8498033..dcc8849be3e8599c398361a487d22829307bef14:/src/lib.rs diff --git a/src/lib.rs b/src/lib.rs index 790cb68..6019c1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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, Q: AsRef>(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>(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::(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. - // I don’t know why that is, but I think we’ll go with impl AsRef 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, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { internal::symlink_auto(src.as_ref(), dst.as_ref()) @@ -102,17 +72,19 @@ pub fn symlink_auto, Q: AsRef>(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, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { internal::symlink_file(src.as_ref(), dst.as_ref()) @@ -120,17 +92,19 @@ pub fn symlink_file, Q: AsRef>(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, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { internal::symlink_dir(src.as_ref(), dst.as_ref()) @@ -144,15 +118,13 @@ pub fn symlink_dir, Q: AsRef>(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>(path: P) -> io::Result<()> { internal::remove_symlink_auto(path) @@ -160,10 +132,9 @@ pub fn remove_symlink_auto>(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>(path: P) -> io::Result<()> { internal::remove_symlink_dir(path) @@ -171,11 +142,8 @@ pub fn remove_symlink_dir>(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>(path: P) -> io::Result<()> { fs::remove_file(path)