mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
* Add document-container crate: container backends and archive codecs * Address PR review: path/prefix split, safe size casts, OPFS stream aborts * Address PR review round 2: mmap read check, UTF8 entry names, prefix normalization * Make MmappedBytes::new fallible so mmap reads can't silently degrade * Address PR review round 3: backend contract uniformity (symlinks, remove, list) * Apply symlink-component check to FolderBackend listing paths * Address PR review: idempotent OPFS delete logging, tar default-features, shared entry-size cap * Fix validate_path doc: dotfiles pass, CurDir/ParentDir rejected * Omit symlink entries from FolderBackend listings for consistency with resolve * Preserve zip I/O errors and reject non-canonical paths in validate_path * Extend archive apis to return the archive writer * Rename document/document-container directory to document/container * Add archive format sniffing and deserialize_auto * Drop temporal hedge from checked_entry_size comment * Tighten verbose doc comments in document-container * Coalesce consecutive same-path OPFS appends to avoid O(n^2) file copies * Review * Update document-container for the deserialize/store_non_blocking renames --------- Co-authored-by: Timon <me@timon.zip>
82 lines
2.7 KiB
Rust
82 lines
2.7 KiB
Rust
#![cfg(any(feature = "zip", feature = "xz"))]
|
|
|
|
use document_container::Container;
|
|
use document_container::archive::{Archive, ArchiveWriter};
|
|
use document_container::backends::folder::FolderBackend;
|
|
use document_container::backends::memory::MemoryBackend;
|
|
|
|
fn entries() -> Vec<(&'static str, &'static [u8])> {
|
|
vec![
|
|
("manifest.json", br#"{"format":"gdd"}"#),
|
|
("document.json", b"{\"registry\":\"...\"}"),
|
|
("history.jsonl", b"{\"rev\":1}\n{\"rev\":2}\n"),
|
|
("resources/abc123", &[0xDE, 0xAD, 0xBE, 0xEF]),
|
|
("resources/xyz789", b"another resource"),
|
|
]
|
|
}
|
|
|
|
fn assert_round_trip(restored: &MemoryBackend) {
|
|
assert_eq!(restored.read("manifest.json").unwrap().as_slice(), br#"{"format":"gdd"}"#);
|
|
assert_eq!(restored.read("document.json").unwrap().as_slice(), b"{\"registry\":\"...\"}");
|
|
assert_eq!(restored.read("history.jsonl").unwrap().as_slice(), b"{\"rev\":1}\n{\"rev\":2}\n");
|
|
assert_eq!(restored.read("resources/abc123").unwrap().as_slice(), &[0xDE, 0xAD, 0xBE, 0xEF]);
|
|
assert_eq!(restored.read("resources/xyz789").unwrap().as_slice(), b"another resource");
|
|
}
|
|
|
|
#[cfg(feature = "zip")]
|
|
#[test]
|
|
fn zip_round_trip() {
|
|
use document_container::archive::Zip;
|
|
use std::io::Cursor;
|
|
|
|
let mut buffer = Cursor::new(Vec::new());
|
|
let mut writer = Zip::writer(&mut buffer).unwrap();
|
|
for (path, bytes) in entries() {
|
|
writer.write_entry(path, bytes).unwrap();
|
|
}
|
|
writer.finish().unwrap();
|
|
|
|
let mut restored = MemoryBackend::new();
|
|
<Zip as Archive>::open(Cursor::new(buffer.get_ref()), &mut restored).unwrap();
|
|
assert_round_trip(&restored);
|
|
}
|
|
|
|
#[cfg(feature = "zip")]
|
|
#[test]
|
|
fn zip_deserialize_streams_into_folder_backend() {
|
|
use document_container::archive::Zip;
|
|
use std::io::Cursor;
|
|
|
|
let mut buffer = Cursor::new(Vec::new());
|
|
let mut writer = Zip::writer(&mut buffer).unwrap();
|
|
for (path, bytes) in entries() {
|
|
writer.write_entry(path, bytes).unwrap();
|
|
}
|
|
writer.finish().unwrap();
|
|
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let mut restored = FolderBackend::create(dir.path()).unwrap();
|
|
<Zip as Archive>::open(Cursor::new(buffer.get_ref()), &mut restored).unwrap();
|
|
|
|
assert_eq!(restored.read("manifest.json").unwrap().as_slice(), br#"{"format":"gdd"}"#);
|
|
assert_eq!(restored.read("resources/abc123").unwrap().as_slice(), &[0xDE, 0xAD, 0xBE, 0xEF]);
|
|
}
|
|
|
|
#[cfg(feature = "xz")]
|
|
#[test]
|
|
fn xz_round_trip() {
|
|
use document_container::archive::Xz;
|
|
use std::io::Cursor;
|
|
|
|
let mut buffer = Cursor::new(Vec::new());
|
|
let mut writer = Xz::writer(&mut buffer).unwrap();
|
|
for (path, bytes) in entries() {
|
|
writer.write_entry(path, bytes).unwrap();
|
|
}
|
|
writer.finish().unwrap();
|
|
|
|
let mut restored = MemoryBackend::new();
|
|
<Xz as Archive>::open(Cursor::new(buffer.get_ref()), &mut restored).unwrap();
|
|
assert_round_trip(&restored);
|
|
}
|