mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Merge 56eeee63e9
into 3890e12c9e
This commit is contained in:
commit
94e9fe58bd
2 changed files with 36 additions and 1 deletions
|
@ -243,6 +243,7 @@ Lee Doughty <32392044+leedoughty@users.noreply.github.com>
|
||||||
memchr <memchr@proton.me>
|
memchr <memchr@proton.me>
|
||||||
Max Romanowski <maxr777@proton.me>
|
Max Romanowski <maxr777@proton.me>
|
||||||
Aldlss <ayaldlss@gmail.com>
|
Aldlss <ayaldlss@gmail.com>
|
||||||
|
Amanda Sternberg <mandis.sternberg@gmail.com>
|
||||||
|
|
||||||
********************
|
********************
|
||||||
|
|
||||||
|
|
|
@ -320,6 +320,20 @@ pub(crate) fn mtime_as_i64<P: AsRef<Path>>(path: P) -> io::Result<i64> {
|
||||||
.as_millis() as i64)
|
.as_millis() as i64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// On POSIX this is EXDEV (error 18) and on Windows it is ERROR_NOT_SAME_DEVICE
|
||||||
|
/// (error 17).
|
||||||
|
pub(crate) fn safe_rename(src: &Path, dst: &Path) -> io::Result<()> {
|
||||||
|
match fs::rename(src, dst) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) if e.kind() == io::ErrorKind::CrossesDevices => {
|
||||||
|
fs::copy(src, dst)?;
|
||||||
|
fs::remove_file(src)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remove_files<S>(media_folder: &Path, files: &[S]) -> Result<()>
|
pub fn remove_files<S>(media_folder: &Path, files: &[S]) -> Result<()>
|
||||||
where
|
where
|
||||||
S: AsRef<str> + std::fmt::Debug,
|
S: AsRef<str> + std::fmt::Debug,
|
||||||
|
@ -344,7 +358,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// move file to trash, clobbering any existing file with the same name
|
// move file to trash, clobbering any existing file with the same name
|
||||||
fs::rename(&src_path, &dst_path)?;
|
safe_rename(&src_path, &dst_path)?;
|
||||||
|
|
||||||
// mark it as modified, so we can expire it in the future
|
// mark it as modified, so we can expire it in the future
|
||||||
let secs = time::SystemTime::now();
|
let secs = time::SystemTime::now();
|
||||||
|
@ -437,9 +451,11 @@ pub(crate) fn data_for_file(media_folder: &Path, fname: &str) -> Result<Option<V
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
|
use super::safe_rename;
|
||||||
use crate::media::files::add_data_to_folder_uniquely;
|
use crate::media::files::add_data_to_folder_uniquely;
|
||||||
use crate::media::files::add_hash_suffix_to_file_stem;
|
use crate::media::files::add_hash_suffix_to_file_stem;
|
||||||
use crate::media::files::normalize_filename;
|
use crate::media::files::normalize_filename;
|
||||||
|
@ -550,4 +566,22 @@ mod test {
|
||||||
Cow::<str>::Owned(format!("{}_", " ".repeat(MAX_MEDIA_FILENAME_LENGTH - 2)))
|
Cow::<str>::Owned(format!("{}_", " ".repeat(MAX_MEDIA_FILENAME_LENGTH - 2)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn safe_rename_moves_file() {
|
||||||
|
// This test only verifies that ´safe_rename´calls back to copy+remove logic
|
||||||
|
// when needed. It does not simulate a real cross-device move.
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let src = dir.path().join("test_src.txt");
|
||||||
|
let dst = dir.path().join("test_dst.txt");
|
||||||
|
|
||||||
|
fs::write(&src, b"hello world").unwrap();
|
||||||
|
safe_rename(&src, &dst).unwrap();
|
||||||
|
|
||||||
|
assert!(dst.exists());
|
||||||
|
assert!(!src.exists());
|
||||||
|
let contents = fs::read(&dst).unwrap();
|
||||||
|
assert_eq!(contents, b"hello world");
|
||||||
|
|
||||||
|
fs::remove_file(&dst).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue