LCOV - code coverage report
Current view: top level - libs/utils/src/fs_ext - rename_noreplace.rs (source / functions) Coverage Total Hit
Test: 8b13a09a5c233d98abd4a0d3e59157e7db16d6fd.info Lines: 96.6 % 87 84
Test Date: 2024-11-21 10:53:51 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         1733 : pub fn rename_noreplace<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
       7         1733 :     src: &P1,
       8         1733 :     dst: &P2,
       9         1733 : ) -> nix::Result<()> {
      10         1733 :     {
      11         1733 :         #[cfg(target_os = "linux")]
      12         1733 :         {
      13         1733 :             nix::fcntl::renameat2(
      14         1733 :                 None,
      15         1733 :                 src,
      16         1733 :                 None,
      17         1733 :                 dst,
      18         1733 :                 nix::fcntl::RenameFlags::RENAME_NOREPLACE,
      19         1733 :             )
      20         1733 :         }
      21         1733 :         #[cfg(target_os = "macos")]
      22         1733 :         {
      23         1733 :             let res = src.with_nix_path(|src| {
      24         1733 :                 dst.with_nix_path(|dst|
      25         1733 :                     // SAFETY: `src` and `dst` are valid C strings as per the NixPath trait and they outlive the call to renamex_np.
      26         1733 :                     unsafe {
      27         1733 :                         nix::libc::renamex_np(src.as_ptr(), dst.as_ptr(), nix::libc::RENAME_EXCL)
      28         1733 :                 })
      29         1733 :             })??;
      30         1733 :             nix::errno::Errno::result(res).map(drop)
      31         1733 :         }
      32         1733 :         #[cfg(not(any(target_os = "linux", target_os = "macos")))]
      33         1733 :         {
      34         1733 :             std::compile_error!("OS does not support no-replace renames");
      35         1733 :         }
      36         1733 :     }
      37         1733 : }
      38              : 
      39              : #[cfg(test)]
      40              : mod test {
      41              :     use std::{fs, path::PathBuf};
      42              : 
      43              :     use super::*;
      44              : 
      45            3 :     fn testdir() -> camino_tempfile::Utf8TempDir {
      46            3 :         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            3 :             None => camino_tempfile::tempdir().unwrap(),
      52              :         }
      53            3 :     }
      54              : 
      55              :     #[test]
      56            1 :     fn test_absolute_paths() {
      57            1 :         let testdir = testdir();
      58            1 :         println!("testdir: {}", testdir.path());
      59            1 : 
      60            1 :         let src = testdir.path().join("src");
      61            1 :         let dst = testdir.path().join("dst");
      62            1 : 
      63            1 :         fs::write(&src, b"").unwrap();
      64            1 :         fs::write(&dst, b"").unwrap();
      65            1 : 
      66            1 :         let src = src.canonicalize().unwrap();
      67            1 :         assert!(src.is_absolute());
      68            1 :         let dst = dst.canonicalize().unwrap();
      69            1 :         assert!(dst.is_absolute());
      70              : 
      71            1 :         let result = rename_noreplace(&src, &dst);
      72            1 :         assert_eq!(result.unwrap_err(), nix::Error::EEXIST);
      73            1 :     }
      74              : 
      75              :     #[test]
      76            1 :     fn test_relative_paths() {
      77            1 :         let testdir = testdir();
      78            1 :         println!("testdir: {}", testdir.path());
      79            1 : 
      80            1 :         // this is fine because we run in nextest => process per test
      81            1 :         std::env::set_current_dir(testdir.path()).unwrap();
      82            1 : 
      83            1 :         let src = PathBuf::from("src");
      84            1 :         let dst = PathBuf::from("dst");
      85            1 : 
      86            1 :         fs::write(&src, b"").unwrap();
      87            1 :         fs::write(&dst, b"").unwrap();
      88            1 : 
      89            1 :         let result = rename_noreplace(&src, &dst);
      90            1 :         assert_eq!(result.unwrap_err(), nix::Error::EEXIST);
      91            1 :     }
      92              : 
      93              :     #[test]
      94            1 :     fn test_works_when_not_exists() {
      95            1 :         let testdir = testdir();
      96            1 :         println!("testdir: {}", testdir.path());
      97            1 : 
      98            1 :         let src = testdir.path().join("src");
      99            1 :         let dst = testdir.path().join("dst");
     100            1 : 
     101            1 :         fs::write(&src, b"content").unwrap();
     102            1 : 
     103            1 :         rename_noreplace(src.as_std_path(), dst.as_std_path()).unwrap();
     104            1 :         assert_eq!(
     105            1 :             "content",
     106            1 :             String::from_utf8(std::fs::read(&dst).unwrap()).unwrap()
     107            1 :         );
     108            1 :     }
     109              : }
        

Generated by: LCOV version 2.1-beta