Files
ElyOffice/crates/ely-files/src/recent.rs
T
ZacharyZhang-NY b7fca0146d Ely Office: six offline-first desktop apps in Rust and GPUI
ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git),
ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
2026-10-08 21:20:08 -04:00

76 lines
2.3 KiB
Rust

//! Recently opened paths, kept in a JSON file.
use std::{
io,
path::{Path, PathBuf},
};
/// How many paths the list keeps.
const KEEP: usize = 20;
/// Recently opened paths, newest first.
pub fn recent(file: &Path) -> io::Result<Vec<PathBuf>> {
match std::fs::read(file) {
Ok(bytes) => serde_json::from_slice(&bytes)
.map_err(|error| io::Error::other(format!("{}: {error}", file.display()))),
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(Vec::new()),
Err(error) => Err(error),
}
}
/// Takes `root` out of the list.
pub fn forget(file: &Path, root: &Path) -> io::Result<()> {
let mut list = recent(file)?;
list.retain(|known| known != root);
crate::save_atomic(
file,
&serde_json::to_vec(&list).map_err(io::Error::other)?,
0,
)
.map(drop)
}
/// Puts `root` first in the list and writes it.
pub fn remember(file: &Path, root: &Path) -> io::Result<Vec<PathBuf>> {
let mut list = recent(file)?;
list.retain(|known| known != root);
list.insert(0, root.to_path_buf());
list.truncate(KEEP);
if let Some(dir) = file.parent() {
std::fs::create_dir_all(dir)?;
}
crate::save_atomic(
file,
&serde_json::to_vec(&list).map_err(io::Error::other)?,
0,
)?;
Ok(list)
}
#[cfg(test)]
mod tests {
use std::path::{Path, PathBuf};
use super::{forget, recent, remember};
#[test]
fn the_list_puts_the_newest_first_and_keeps_twenty() {
let dir = tempfile::tempdir().expect("a temp dir");
let file = dir.path().join("data/recent.json");
assert!(recent(&file).expect("none yet").is_empty());
for n in 0..25 {
remember(&file, Path::new(&format!("/r/{n}"))).expect("remembers");
}
let list = remember(&file, Path::new("/r/10")).expect("remembers");
assert_eq!(list.len(), 20);
assert_eq!(list[..2], [PathBuf::from("/r/10"), PathBuf::from("/r/24")]);
forget(&file, Path::new("/r/10")).expect("forgets");
assert_eq!(recent(&file).expect("reads")[0], PathBuf::from("/r/24"));
std::fs::write(&file, "not json").expect("writes");
assert!(
remember(&file, Path::new("/r/1")).is_err(),
"a broken list is not overwritten"
);
}
}