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

Generated by: LCOV version 2.1-beta