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.
This commit is contained in:
ZacharyZhang-NY committed 2026-10-08 21:20:08 -04:00
commit b7fca0146d
817 files changed
+143101

No files matched your search

+67
View File
@@ -0,0 +1,67 @@
//! A full disk refuses the save; the original stays (§20.2).
use std::{path::Path, process::Command};
use super::save_atomic;
/// A 4 MB disk image, attached; detached when dropped.
struct Volume(tempfile::TempDir);
impl Volume {
fn new() -> Option<Self> {
let dir = tempfile::tempdir().expect("a temp dir");
let image = dir.path().join("full.dmg");
let made = Command::new("hdiutil")
.args(["create", "-quiet", "-size", "4m", "-fs", "HFS+", "-volname", "elyfull"])
.arg(&image)
.status();
if !made.is_ok_and(|status| status.success()) {
return None;
}
let mount = dir.path().join("mount");
std::fs::create_dir(&mount).expect("the mount point");
let attached = Command::new("hdiutil")
.args(["attach", "-quiet", "-nobrowse", "-mountpoint"])
.arg(&mount)
.arg(&image)
.status()
.expect("hdiutil runs");
assert!(attached.success(), "the image attaches");
Some(Volume(dir))
}
fn root(&self) -> std::path::PathBuf {
self.0.path().join("mount")
}
}
impl Drop for Volume {
fn drop(&mut self) {
let detached = Command::new("hdiutil").args(["detach", "-quiet", "-force"]).arg(self.root()).status();
if !detached.is_ok_and(|status| status.success()) {
eprintln!("the test image stays attached at {}", self.root().display());
}
}
}
/// Writes until the volume refuses, then frees nothing.
fn fill(root: &Path) {
let block = vec![7u8; 64 * 1024];
let mut filler = std::fs::File::create(root.join("filler")).expect("the filler");
while std::io::Write::write_all(&mut filler, &block).is_ok() {}
}
#[test]
fn a_full_disk_refuses_the_save_and_keeps_the_original() {
let Some(volume) = Volume::new() else {
return eprintln!("SKIP: no hdiutil");
};
let target = volume.root().join("keep.md");
std::fs::write(&target, "original").expect("the original");
fill(&volume.root());
let failed = save_atomic(&target, &vec![b'x'; 256 * 1024], 1).expect_err("no room");
assert_eq!(failed.kind(), std::io::ErrorKind::StorageFull, "{failed}");
assert_eq!(std::fs::read_to_string(&target).expect("reads"), "original");
let left: Vec<_> = std::fs::read_dir(volume.root()).expect("lists").flatten().map(|entry| entry.file_name()).collect();
assert_eq!(left.len(), 2, "no half-written file stays: {left:?}");
}
+500
View File
@@ -0,0 +1,500 @@
use super::*;
use crate::Session;
fn write(path: &Path, body: &str) {
std::fs::write(path, body).expect("the fixture file writes");
}
fn read(path: &Path) -> String {
std::fs::read_to_string(path).expect("the file reads")
}
#[test]
fn an_atomic_save_lands_and_fingerprints() {
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("doc.md");
let written = save_atomic(&target, b"body", 3).expect("the save lands");
assert_eq!(read(&target), "body");
assert_eq!(written, Fingerprint::of(&target).expect("the fingerprint"));
assert_eq!(written.len, 4);
}
#[test]
fn a_bare_relative_name_saves_and_probes() {
let dir = tempfile::tempdir().expect("a temp dir");
let here = std::env::current_dir().expect("the cwd");
std::env::set_current_dir(dir.path()).expect("into the temp dir");
let result = save_atomic(Path::new("report.md"), b"0", 0)
.map(|_| atomic_capable(Path::new("report.md")));
std::env::set_current_dir(here).expect("back to the cwd");
assert!(result.expect("the save lands").expect("the probe"));
assert_eq!(read(&dir.path().join("report.md")), "0");
}
#[cfg(unix)]
#[test]
fn a_failed_write_keeps_the_original() {
use std::os::unix::fs::PermissionsExt;
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("keep.md");
write(&target, "original");
std::fs::set_permissions(dir.path(), std::fs::Permissions::from_mode(0o555))
.expect("the lockdown");
let failed = save_atomic(&target, b"new", 1);
std::fs::set_permissions(dir.path(), std::fs::Permissions::from_mode(0o755))
.expect("the restore");
assert!(failed.is_err());
assert_eq!(read(&target), "original");
}
#[cfg(unix)]
#[test]
fn a_save_keeps_the_original_permissions() {
use std::os::unix::fs::PermissionsExt;
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("shared.md");
write(&target, "0");
std::fs::set_permissions(&target, std::fs::Permissions::from_mode(0o640))
.expect("the group read");
save_atomic(&target, b"1", 1).expect("the save lands");
let mode = std::fs::metadata(&target)
.expect("the metadata")
.permissions()
.mode()
& 0o777;
assert_eq!(mode, 0o640);
}
#[test]
fn paths_keep_spaces_cjk_and_case() {
let dir = tempfile::tempdir().expect("a temp dir");
let name = "报告 Final 版本 — Copy.TXT";
let target = dir.path().join(name);
save_atomic(&target, b"0", 0).expect("the save lands");
let listed: Vec<String> = std::fs::read_dir(dir.path())
.expect("the dir lists")
.map(|entry| {
entry
.expect("an entry")
.file_name()
.to_string_lossy()
.into_owned()
})
.collect();
assert_eq!(listed, vec![name.to_string()]);
}
#[test]
fn a_backup_save_keeps_the_previous_file() {
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("net.md");
write(&target, "previous");
let (_, backup) = save_with_backup(&target, b"new", 1).expect("the save lands");
let backup = backup.expect("a backup exists");
assert_eq!(read(&target), "new");
assert_eq!(read(&backup), "previous");
assert_eq!(backup, dir.path().join("net.md.bak"));
}
#[cfg(unix)]
#[test]
fn a_backup_never_truncates_or_follows_links() {
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("report.md");
write(&target, "previous");
let taken = dir.path().join("report.md.bak");
std::fs::hard_link(&target, &taken).expect("the hard link");
let other = dir.path().join("other.md");
write(&other, "other");
let linked = dir.path().join("report.md-1.bak");
std::os::unix::fs::symlink(&other, &linked).expect("the symlink");
let (_, backup) = save_with_backup(&target, b"new", 1).expect("the save lands");
let backup = backup.expect("a backup exists");
assert!(
!matches!(backup.file_name(), Some(n) if n == "report.md.bak" || n == "report.md-1.bak")
);
assert_eq!(read(&target), "new");
assert_eq!(read(&other), "other");
assert_eq!(read(&backup), "previous");
}
#[test]
fn a_backup_name_fits_long_file_names() {
let dir = tempfile::tempdir().expect("a temp dir");
let name = format!("{}.md", "x".repeat(250));
let target = dir.path().join(name);
write(&target, "previous");
let (_, backup) = save_with_backup(&target, b"new", 1).expect("the save lands");
let backup = backup.expect("a backup exists");
assert_eq!(read(&backup), "previous");
let len = backup.file_name().expect("a name").len();
assert!(len <= 255, "the backup name fits: {len}");
}
#[test]
fn an_export_is_independent_and_never_overwrites() {
let dir = tempfile::tempdir().expect("a temp dir");
let editing = dir.path().join("report.md");
write(&editing, "session");
let out = dir.path().join("out.pdf");
save_atomic(&out, b"%PDF", 2).expect("the export lands");
assert_eq!(read(&editing), "session");
let refused = export(&editing, Some(editing.as_path()), b"%PDF", 2);
assert!(refused.is_err());
assert_eq!(read(&editing), "session");
assert!(export(&out, Some(editing.as_path()), b"%PDF2", 2).is_ok());
}
#[cfg(unix)]
#[test]
fn an_export_refuses_path_aliases_of_the_session() {
let dir = tempfile::tempdir().expect("a temp dir");
let editing = dir.path().join("report.md");
write(&editing, "session");
let child = dir.path().join("child");
std::fs::create_dir(&child).expect("the child dir");
let dotted = child.join("..").join("report.md");
assert!(export(&dotted, Some(editing.as_path()), b"%PDF", 2).is_err());
let linked = dir.path().join("linked");
std::os::unix::fs::symlink(&editing, &linked).expect("the symlink");
assert!(export(&linked, Some(editing.as_path()), b"%PDF", 2).is_err());
let alias = dir.path().join("alias.md");
std::fs::hard_link(&editing, &alias).expect("the hard link");
assert!(export(&alias, Some(editing.as_path()), b"%PDF", 2).is_err());
assert_eq!(read(&editing), "session");
}
#[test]
fn an_import_copies_and_never_deletes() {
let dir = tempfile::tempdir().expect("a temp dir");
let source = dir.path().join("photo.png");
write(&source, "png-bytes");
let attachments = dir.path().join("attachments");
let first = import_attachment(&source, &attachments).expect("the import lands");
assert!(source.try_exists().expect("the source check"));
assert_eq!(read(&source), "png-bytes");
assert_eq!(read(&first), "png-bytes");
let second = import_attachment(&source, &attachments).expect("the second import");
assert_ne!(first, second);
assert_eq!(read(&first), "png-bytes");
assert_eq!(read(&second), "png-bytes");
}
#[test]
fn a_numbered_import_name_fits_long_file_names() {
let dir = tempfile::tempdir().expect("a temp dir");
let source = dir.path().join(format!("{}.png", "x".repeat(251)));
write(&source, "png");
let attachments = dir.path().join("attachments");
import_attachment(&source, &attachments).expect("the first import");
let second = import_attachment(&source, &attachments).expect("the second import");
let len = second.file_name().expect("a name").len();
assert!(len <= 255, "the numbered name fits: {len}");
assert_eq!(read(&second), "png");
}
#[test]
fn the_save_flow_checks_external_changes_first() {
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("flow.md");
write(&target, "0");
let mut session = Session::load(target.clone()).expect("the file loads");
session.edit();
write(&target, "external");
let outcome = save(&mut session, b"local");
assert!(matches!(outcome, Err(SaveError::Conflict)));
assert_eq!(read(&target), "external");
assert_eq!(session.revision(), 1);
assert_eq!(session.state(), crate::SaveState::ExternalModified);
let ticket = session.begin_save_overwrite().expect("keep-local starts");
write(&target, "local");
let written = Fingerprint::of(&target).expect("the fingerprint");
session
.finish_save(ticket, target, written)
.expect("the overwrite lands");
assert_eq!(session.state(), crate::SaveState::Saved);
}
#[test]
fn the_save_flow_writes_and_confirms() {
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("ok.md");
write(&target, "0");
let mut session = Session::load(target).expect("the file loads");
session.edit();
let saved = save(&mut session, b"edited").expect("the save lands");
assert_eq!(read(&saved.path), "edited");
assert_eq!(saved.backup, None);
assert_eq!(session.state(), crate::SaveState::Saved);
}
#[test]
fn external_change_detection_follows_the_fingerprint() {
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("x.md");
write(&target, "0");
let mut session = Session::load(target.clone()).expect("the file loads");
assert!(!session.external_changed().expect("the check"));
write(&target, "longer content");
assert!(session.external_changed().expect("the check"));
session.reload().expect("the file reloads");
assert!(!session.external_changed().expect("the check"));
let fresh = Session::new_file();
assert!(!fresh.external_changed().expect("the check"));
}
#[test]
fn local_volumes_are_atomic_capable() {
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("local.md");
assert!(atomic_capable(&target).expect("the probe"));
}
#[test]
fn the_save_flow_keeps_a_backup_on_a_limited_volume() {
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("net.md");
write(&target, "previous");
let mut session = Session::load(target.clone()).expect("the file loads");
session.edit();
let saved = save_mode(&mut session, &target, b"new", true).expect("the save lands");
let backup = saved.backup.expect("a backup on a limited volume");
assert_eq!(read(&target), "new");
assert_eq!(read(&backup), "previous");
assert_eq!(session.state(), crate::SaveState::Saved);
}
#[cfg(target_os = "macos")]
#[test]
fn a_save_keeps_the_original_acl() {
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("acl.md");
write(&target, "0");
let chmod = std::process::Command::new("chmod")
.args(["+a", "nobody deny read", target.to_str().expect("utf8")])
.status()
.expect("chmod runs");
assert!(chmod.success());
save_atomic(&target, b"1", 1).expect("the save lands");
let listed = std::process::Command::new("/bin/ls")
.args(["-le", target.to_str().expect("utf8")])
.output()
.expect("ls runs");
let text = String::from_utf8_lossy(&listed.stdout).into_owned();
assert!(text.contains("deny read"), "the ACL survived: {text}");
}
#[cfg(unix)]
#[test]
fn a_backup_keeps_the_source_permissions() {
use std::os::unix::fs::PermissionsExt;
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("private.md");
write(&target, "secret");
std::fs::set_permissions(&target, std::fs::Permissions::from_mode(0o600))
.expect("the private mode");
let (_, backup) = save_with_backup(&target, b"new", 1).expect("the save lands");
let mode = std::fs::metadata(backup.expect("a backup"))
.expect("the metadata")
.permissions()
.mode()
& 0o777;
assert_eq!(mode, 0o600);
}
#[cfg(unix)]
#[test]
fn a_save_through_a_symlink_keeps_the_link() {
let dir = tempfile::tempdir().expect("a temp dir");
let real = dir.path().join("report.md");
let alias = dir.path().join("alias.md");
write(&real, "0");
std::os::unix::fs::symlink(&real, &alias).expect("the symlink");
let mut session = Session::load(alias.clone()).expect("the file loads");
session.edit();
save(&mut session, b"edited").expect("the save lands");
assert_eq!(read(&real), "edited");
let kind = std::fs::symlink_metadata(&alias)
.expect("the metadata")
.file_type();
assert!(kind.is_symlink(), "the link survives the save");
assert_eq!(session.state(), crate::SaveState::Saved);
}
#[test]
fn backups_never_run_out_of_names() {
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("report.md");
write(&target, "0");
for n in 0..105u32 {
save_with_backup(&target, format!("v{n}").as_bytes(), n as u64).expect("the save lands");
}
let names = std::fs::read_dir(dir.path())
.expect("the dir lists")
.filter_map(|entry| entry.ok())
.filter(|entry| entry.file_name().to_string_lossy().ends_with(".bak"))
.count();
assert_eq!(names, 105);
}
#[test]
fn a_long_extension_import_fits_twice() {
let dir = tempfile::tempdir().expect("a temp dir");
let source = dir.path().join(format!("a.{}", "x".repeat(253)));
write(&source, "png");
let attachments = dir.path().join("attachments");
import_attachment(&source, &attachments).expect("the first import");
let second = import_attachment(&source, &attachments).expect("the second import");
let len = second.file_name().expect("a name").len();
assert!(len <= 255, "the numbered name fits: {len}");
assert_eq!(read(&second), "png");
}
#[cfg(unix)]
#[test]
fn a_save_through_a_cross_dir_link_probes_the_real_volume() {
let dir = tempfile::tempdir().expect("a temp dir");
let home = dir.path().join("home");
let cloud = dir.path().join("cloud");
std::fs::create_dir(&home).expect("the home dir");
std::fs::create_dir(&cloud).expect("the cloud dir");
let real = cloud.join("report.md");
let alias = home.join("alias.md");
write(&real, "previous");
std::os::unix::fs::symlink(&real, &alias).expect("the symlink");
let mut session = Session::load(alias).expect("the file loads");
session.edit();
let limited = resolved(&real).expect("the resolved target");
let probe = move |path: &Path| Ok(path != limited);
let saved = save_with_probe(&mut session, b"new", probe).expect("the save lands");
let backup = saved.backup.expect("a backup on the limited volume");
let real_dir = resolved(&cloud).expect("the resolved cloud dir");
assert_eq!(backup.parent(), Some(real_dir.as_path()));
assert_eq!(read(&backup), "previous");
assert_eq!(read(&real), "new");
assert_eq!(session.state(), crate::SaveState::Saved);
}
#[test]
fn a_full_length_name_gets_a_backup() {
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join("x".repeat(255));
write(&target, "previous");
let (_, backup) = save_with_backup(&target, b"new", 1).expect("the save lands");
let backup = backup.expect("a backup exists");
let len = backup.file_name().expect("a name").len();
assert!(len <= 255, "the backup name fits: {len}");
assert_eq!(read(&backup), "previous");
}
#[test]
fn a_long_name_gets_distinct_backups_every_time() {
let dir = tempfile::tempdir().expect("a temp dir");
let target = dir.path().join(format!("{}.md", "x".repeat(250)));
write(&target, "0");
let mut names = std::collections::HashSet::new();
for n in 0..12u32 {
let (_, backup) =
save_with_backup(&target, format!("v{n}").as_bytes(), n as u64).expect("the save");
let name = backup
.expect("a backup")
.file_name()
.expect("a name")
.to_owned();
assert!(name.len() <= 255, "the backup name fits");
assert!(names.insert(name), "every backup name differs");
}
}
#[test]
fn a_full_length_attachment_imports_twice() {
let dir = tempfile::tempdir().expect("a temp dir");
let source = dir.path().join("x".repeat(255));
write(&source, "png");
let attachments = dir.path().join("attachments");
let first = import_attachment(&source, &attachments).expect("the first import");
let second = import_attachment(&source, &attachments).expect("the second import");
assert_ne!(first, second);
let len = second.file_name().expect("a name").len();
assert!(len <= 255, "the numbered name fits: {len}");
assert_eq!(read(&second), "png");
}
#[test]
fn save_as_gives_a_new_file_its_path_and_leaves_the_source() {
let dir = tempfile::tempdir().expect("a temp dir");
let mut session = Session::new_file();
session.edit();
let first = dir.path().join("Untitled.docx");
let saved = save_as(&mut session, &first, b"one").expect("the first save");
assert_eq!(read(&first), "one");
assert_eq!(
session.path(),
Some(saved.path.as_path()),
"the session takes the path"
);
assert_eq!(session.state(), SaveState::Saved);
session.edit();
let second = dir.path().join("copy.odt");
save_as(&mut session, &second, b"two").expect("the second save");
assert_eq!(read(&first), "one", "the source stays");
assert_eq!(read(&second), "two");
assert!(
session
.path()
.is_some_and(|path| path.ends_with("copy.odt"))
);
session.edit();
save(&mut session, b"three").expect("a plain save follows the new path");
assert_eq!(read(&second), "three");
}
#[test]
fn save_as_onto_its_own_file_checks_for_outside_changes() {
let dir = tempfile::tempdir().expect("a temp dir");
let own = dir.path().join("own.docx");
let mut session = Session::new_file();
session.edit();
save_as(&mut session, &own, b"mine").expect("the first save");
session.edit();
std::thread::sleep(std::time::Duration::from_millis(20));
write(&own, "EXTERNAL REVISION");
let alias = dir.path().join("alias.docx");
#[cfg(unix)]
std::os::unix::fs::symlink(&own, &alias).expect("the alias");
#[cfg(not(unix))]
let alias = own.clone();
assert!(matches!(
save_as(&mut session, &alias, b"local"),
Err(SaveError::Conflict)
));
assert_eq!(read(&own), "EXTERNAL REVISION", "the outside change stays");
let copy = dir.path().join("local-copy.docx");
save_as(&mut session, &copy, b"local").expect("a new file keeps the local version");
assert_eq!(read(&copy), "local");
assert_eq!(read(&own), "EXTERNAL REVISION");
assert_eq!(
session.state(),
SaveState::Saved,
"the session moved to the copy"
);
}
#[test]
fn overwrite_resolves_a_conflict_with_the_local_version() {
let dir = tempfile::tempdir().expect("a temp dir");
let own = dir.path().join("own.odt");
let mut session = Session::new_file();
session.edit();
save_as(&mut session, &own, b"mine").expect("the first save");
session.edit();
std::thread::sleep(std::time::Duration::from_millis(20));
write(&own, "theirs");
assert!(matches!(
save(&mut session, b"local"),
Err(SaveError::Conflict)
));
save_overwrite(&mut session, b"local").expect("keep-local overwrites");
assert_eq!(read(&own), "local");
assert_eq!(session.state(), SaveState::Saved);
}