LCOV - code coverage report
Current view: top level - libs/utils/src/fs_ext - rename_noreplace.rs (source / functions) Coverage Total Hit
Test: 465a86b0c1fda0069b3e0f6c1c126e6b635a1f72.info Lines: 96.6 % 87 84
Test Date: 2024-06-25 15:47:26 Functions: 85.7 % 7 6

            Line data    Source code
       1              : use nix::NixPath;
       2              : 
       3              : /// Rename a file without replacing an existing file.
       4              : ///
       5              : /// This is a wrapper around platform-specific APIs.
       6         1564 : pub fn rename_noreplace<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
       7         1564 :     src: &P1,
       8         1564 :     dst: &P2,
       9         1564 : ) -> nix::Result<()> {
      10         1564 :     {
      11         1564 :         #[cfg(target_os = "linux")]
      12         1564 :         {
      13         1564 :             nix::fcntl::renameat2(
      14         1564 :                 None,
      15         1564 :                 src,
      16         1564 :                 None,
      17         1564 :                 dst,
      18         1564 :                 nix::fcntl::RenameFlags::RENAME_NOREPLACE,
      19         1564 :             )
      20         1564 :         }
      21         1564 :         #[cfg(target_os = "macos")]
      22         1564 :         {
      23         1564 :             let res = src.with_nix_path(|src| {
      24         1564 :                 dst.with_nix_path(|dst|
      25         1564 :                     // SAFETY: `src` and `dst` are valid C strings as per the NixPath trait and they outlive the call to renamex_np.
      26         1564 :                     unsafe {
      27         1564 :                         nix::libc::renamex_np(src.as_ptr(), dst.as_ptr(), nix::libc::RENAME_EXCL)
      28         1564 :                 })
      29         1564 :             })??;
      30         1564 :             nix::errno::Errno::result(res).map(drop)
      31         1564 :         }
      32         1564 :         #[cfg(not(any(target_os = "linux", target_os = "macos")))]
      33         1564 :         {
      34         1564 :             std::compile_error!("OS does not support no-replace renames");
      35         1564 :         }
      36         1564 :     }
      37         1564 : }
      38              : 
      39              : #[cfg(test)]
      40              : mod test {
      41              :     use std::{fs, path::PathBuf};
      42              : 
      43              :     use super::*;
      44              : 
      45            6 :     fn testdir() -> camino_tempfile::Utf8TempDir {
      46            6 :         match crate::env::var("NEON_UTILS_RENAME_NOREPLACE_TESTDIR") {
      47            0 :             Some(path) => {
      48            0 :                 let path: camino::Utf8PathBuf = path;
      49            0 :                 camino_tempfile::tempdir_in(path).unwrap()
      50              :             }
      51            6 :             None => camino_tempfile::tempdir().unwrap(),
      52              :         }
      53            6 :     }
      54              : 
      55              :     #[test]
      56            2 :     fn test_absolute_paths() {
      57            2 :         let testdir = testdir();
      58            2 :         println!("testdir: {}", testdir.path());
      59            2 : 
      60            2 :         let src = testdir.path().join("src");
      61            2 :         let dst = testdir.path().join("dst");
      62            2 : 
      63            2 :         fs::write(&src, b"").unwrap();
      64            2 :         fs::write(&dst, b"").unwrap();
      65            2 : 
      66            2 :         let src = src.canonicalize().unwrap();
      67            2 :         assert!(src.is_absolute());
      68            2 :         let dst = dst.canonicalize().unwrap();
      69            2 :         assert!(dst.is_absolute());
      70              : 
      71            2 :         let result = rename_noreplace(&src, &dst);
      72            2 :         assert_eq!(result.unwrap_err(), nix::Error::EEXIST);
      73            2 :     }
      74              : 
      75              :     #[test]
      76            2 :     fn test_relative_paths() {
      77            2 :         let testdir = testdir();
      78            2 :         println!("testdir: {}", testdir.path());
      79            2 : 
      80            2 :         // this is fine because we run in nextest => process per test
      81            2 :         std::env::set_current_dir(testdir.path()).unwrap();
      82            2 : 
      83            2 :         let src = PathBuf::from("src");
      84            2 :         let dst = PathBuf::from("dst");
      85            2 : 
      86            2 :         fs::write(&src, b"").unwrap();
      87            2 :         fs::write(&dst, b"").unwrap();
      88            2 : 
      89            2 :         let result = rename_noreplace(&src, &dst);
      90            2 :         assert_eq!(result.unwrap_err(), nix::Error::EEXIST);
      91            2 :     }
      92              : 
      93              :     #[test]
      94            2 :     fn test_works_when_not_exists() {
      95            2 :         let testdir = testdir();
      96            2 :         println!("testdir: {}", testdir.path());
      97            2 : 
      98            2 :         let src = testdir.path().join("src");
      99            2 :         let dst = testdir.path().join("dst");
     100            2 : 
     101            2 :         fs::write(&src, b"content").unwrap();
     102            2 : 
     103            2 :         rename_noreplace(src.as_std_path(), dst.as_std_path()).unwrap();
     104            2 :         assert_eq!(
     105            2 :             "content",
     106            2 :             String::from_utf8(std::fs::read(&dst).unwrap()).unwrap()
     107            2 :         );
     108            2 :     }
     109              : }
        

Generated by: LCOV version 2.1-beta