Files
DarkRoom/ui/dr-ui/src/library/mod.rs
T
dtourolle a1d511fd4b Split library.rs into library/ by area of behaviour
library.rs was 7,729 lines wiring together everything "open a remote
library" touches: scanning, pulling other devices' judgements out of
sidecars found along the way, writing local edits back out to the
sidecar outbox, pushing/reloading XMP by hand, fetching and prefetching
thumbnails and originals, generating thumbnails locally, the metadata
and thumbnail background sweeps, on-disk paths for the catalog and
model files, and reading the grid's cells, spans and rating filter.
Same motivation as the develop.rs split (docs/dev/code-health.md CH-1):
a pure, no-behaviour-change move into one file per area, each under
about 1,500 lines.

Tracing actual call sites rather than trusting the file's physical
layout mattered here: `persist`, `load_folder_etags`, `pull_sidecars`,
`load_sidecar_etags`, `record_sidecar_read` and `apply_judgement` sit
textually beside the XMP push/reload functions but are called only
from `run_scan` (pulling a device's own past judgements out of the
sidecars a scan just walked), so they went to scan.rs and not xmp.rs.
`cells` came out at over 1,800 lines once its tests moved with it and
split further into cells.rs (windowed reads, trash, ordinals) and
spans.rs (collection scope, manual reordering, the capture-time
histogram) -- ten submodules rather than the nine first planned.

Previously-private items reached from a sibling module became
`pub(super)`, narrower than the whole-crate reachability one file gave
them. Tests moved with the code they test; the two test fixtures used
across more than one file (`scanned`, and develop.rs's
`session_with_a_left_half_subject` in the matching commit) joined the
shared `test_support` module alongside the existing `entry`/
`with_images`/`image_ids` helpers. `mod.rs` re-exports every module's
public items under `library::`, including the `pub(crate)`
`test_support` module `repairs.rs` reads its fixtures from, so no file
outside `library` needed a change.

The previous commit split develop.rs the same way; taken alone it left
dr-ui without library.rs, so that intermediate commit does not build on
its own. This one restores it.
2026-09-20 18:21:43 +02:00

126 lines
4.3 KiB
Rust

//! TRACES: FR-CAT-1 | FR-CAT-4 | FR-NC-3 | NFR-P9
//! Opening a remote library: scan → catalog → grid.
//!
//! This is the wire between three pieces that already worked separately —
//! `dr_sync::scan` walks the tree, `dr_catalog` indexes it, and
//! `dr_decode::preview` turns bytes into pixels. Until now the "Open library"
//! button logged its intent and stopped.
//!
//! # Threading
//!
//! Slint's event loop is single-threaded and must never block (NFR-P9), so
//! every network and decode operation runs on a worker thread and results
//! return through an mpsc channel drained by a Slint timer. That is the same
//! shape the login flow uses; it is repeated rather than shared because the
//! message types differ and a generic version would obscure both.
//!
//! # Why thumbnails are fetched, not derived from the scan
//!
//! A scan yields paths and sizes, nothing visual. Each thumbnail costs its own
//! range request, so they are fetched **only for cells the grid actually
//! wants** — never for the whole library up front. On the reference library
//! that is the difference between a few MB and ~370 GB (ARCH §6.7).
//!
//! Split into one module per area (docs/dev/code-health.md CH-1): filtering,
//! sidecar writes, on-disk paths, scanning, XMP, thumbnail fetch and
//! generation, the background sweeps, and reading the grid's cells and spans.
//! `mod.rs` re-exports every module's public items so nothing outside
//! `library` needs to change.
mod cells;
mod filters;
mod paths;
mod scan;
mod sidecar;
mod spans;
mod sweep;
mod thumbnails_fetch;
mod thumbnails_gen;
mod xmp;
pub use cells::*;
pub use filters::*;
pub use paths::*;
pub use scan::*;
pub use sidecar::*;
pub use spans::*;
pub use sweep::*;
pub use thumbnails_fetch::*;
pub use thumbnails_gen::*;
pub use xmp::*;
#[cfg(test)]
pub(crate) mod test_support {
//! Fixtures shared with the modules that build on this one's catalog
//! layout -- `repairs`, whose work lists are read over the same `images`
//! and `remote` rows a scan writes.
use dr_catalog::Catalog;
use dr_sync::{RemoteEntry, RemoteId, RemotePath};
use super::scan::persist;
pub fn entry(path: &str, file_id: u64, size: u64) -> RemoteEntry {
RemoteEntry {
id: RemoteId::Stable(file_id),
path: RemotePath::new(path),
kind: dr_sync::EntryKind::File,
validator: dr_sync::Validator::new("v"),
size,
modified: None,
has_preview: false,
materialised: true,
}
}
/// A catalog with `n` images, ready to file into collections.
pub fn with_images(n: usize) -> Catalog {
let catalog = Catalog::in_memory().unwrap();
let images: Vec<RemoteEntry> = (0..n)
.map(|i| entry(&format!("PhotosRaw/img{i:03}.CR2"), i as u64, 1000))
.collect();
let result = dr_sync::ScanResult {
images,
directories: vec![(RemotePath::new("PhotosRaw"), dr_sync::Validator::new("e"))],
progress: Default::default(),
sidecars: Vec::new(),
};
persist(&catalog, "PhotosRaw", &result).unwrap();
catalog
}
/// A catalog with `n` scanned images, none trashed.
///
/// Shared with `spans`, whose collection-membership tests build on the
/// same "n images, no folders beyond the root" catalog `cells`'s trash
/// tests do.
pub fn scanned(n: u64) -> Catalog {
let catalog = Catalog::in_memory().unwrap();
let images = (1..=n)
.map(|i| entry(&format!("PhotosRaw/IMG_{i:04}.CR2"), 1000 + i, 30_000_000))
.collect();
persist(
&catalog,
"PhotosRaw",
&dr_sync::ScanResult {
images,
directories: vec![(RemotePath::new("PhotosRaw"), dr_sync::Validator::new("e1"))],
progress: Default::default(),
sidecars: Vec::new(),
},
)
.unwrap();
catalog
}
pub fn image_ids(catalog: &Catalog) -> Vec<dr_types::ImageId> {
let mut stmt = catalog
.connection()
.prepare("SELECT id FROM images ORDER BY source_ref")
.unwrap();
stmt.query_map([], |r| Ok(dr_types::ImageId(r.get::<_, i64>(0)? as u64)))
.unwrap()
.map(Result::unwrap)
.collect()
}
}