Skip to content

Commit 752292d

Browse files
committed
feat: add FileSnapshot::into_owned_or_cloned().
That way it's possible to efficiently take ownership if there happens to be only one instance of a snapshot.
1 parent 7bc540e commit 752292d

File tree

9 files changed

+66
-2
lines changed

9 files changed

+66
-2
lines changed

gix-fs/src/snapshot.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// TODO: tests
2-
use std::ops::Deref;
3-
42
use gix_features::threading::{get_mut, get_ref, MutableOnDemand, OwnShared};
3+
use std::ops::Deref;
54

65
/// A structure holding enough information to reload a value if its on-disk representation changes as determined by its modified time.
76
#[derive(Debug)]
@@ -39,6 +38,16 @@ impl<T: Clone + std::fmt::Debug> Clone for FileSnapshot<T> {
3938
}
4039
}
4140

41+
impl<T: Clone + std::fmt::Debug> FileSnapshot<T> {
42+
/// Return the contained instance if nobody else is holding it, or clone it otherwise.
43+
pub fn into_owned_or_cloned(self: OwnShared<Self>) -> T {
44+
match OwnShared::try_unwrap(self) {
45+
Ok(this) => this.value,
46+
Err(this) => this.value.clone(),
47+
}
48+
}
49+
}
50+
4251
/// A snapshot of a resource which is up-to-date in the moment it is retrieved.
4352
pub type SharedFileSnapshot<T> = OwnShared<FileSnapshot<T>>;
4453

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

gix-fs/tests/fs.rs renamed to gix-fs/tests/fs/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ type Result<T = ()> = std::result::Result<T, Box<dyn std::error::Error + Send +
33
mod capabilities;
44
mod dir;
55
mod read_dir;
6+
mod snapshot;
67
mod stack;
File renamed without changes.

gix-fs/tests/fs/snapshot.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use gix_fs::SharedFileSnapshotMut;
2+
use std::path::Path;
3+
4+
#[test]
5+
fn journey() -> Result<(), Box<dyn std::error::Error>> {
6+
let tmp = tempfile::tempdir().unwrap();
7+
if !has_nanosecond_times(tmp.path())? {
8+
return Ok(());
9+
}
10+
11+
let file_path = tmp.path().join("content");
12+
let smut = SharedFileSnapshotMut::<String>::new();
13+
14+
let check = || file_path.metadata().ok()?.modified().ok();
15+
let open = || {
16+
Ok(match std::fs::read_to_string(&file_path) {
17+
Ok(s) => Some(s),
18+
Err(err) if err.kind() == std::io::ErrorKind::NotFound => None,
19+
Err(err) => return Err(err),
20+
})
21+
};
22+
let snap = smut.recent_snapshot(check, open)?;
23+
assert!(snap.is_none());
24+
25+
std::fs::write(&file_path, "content")?;
26+
let snap = smut.recent_snapshot(check, open)?.expect("content read");
27+
assert_eq!(&**snap, "content", "it read the file for the first time");
28+
29+
std::fs::write(&file_path, "change")?;
30+
let snap = smut.recent_snapshot(check, open)?.expect("content read");
31+
assert_eq!(&**snap, "change", "it picks up the change");
32+
33+
std::fs::remove_file(&file_path)?;
34+
let snap = smut.recent_snapshot(check, open)?;
35+
assert!(snap.is_none(), "file deleted, nothing to see here");
36+
37+
std::fs::write(&file_path, "new")?;
38+
let snap = smut.recent_snapshot(check, open)?.expect("content read again");
39+
let owned: String = snap.into_owned_or_cloned();
40+
assert_eq!(owned, "new", "owned versions are possible easily and efficiently");
41+
Ok(())
42+
}
43+
44+
fn has_nanosecond_times(root: &Path) -> std::io::Result<bool> {
45+
let test_file = root.join("nanosecond-test");
46+
47+
std::fs::write(&test_file, "a")?;
48+
let first_time = test_file.metadata()?.modified()?;
49+
50+
std::fs::write(&test_file, "b")?;
51+
let second_time = test_file.metadata()?.modified()?;
52+
53+
Ok(first_time != second_time)
54+
}
File renamed without changes.

0 commit comments

Comments
 (0)