Do not offer an import where one cannot be done
The Import button went into the library header unconditionally, so an Android user got a page that opens, finds nothing, and cannot be pressed — worse than no page at all, because it reads as broken rather than as absent. `dr_plat::imports_supported()` answers the question the interface actually has, which is not "did we find a volume". An empty list on Linux means plug one in; false here means it cannot be done on this device however hard the user tries. It is false on Android for two reasons that both have to be fixed before it changes: there is no mount table to read and no path to type, and nothing implements WritableStorage except LocalStorage. The button is hidden rather than disabled. The buttons beside it come and go with the selection — unavailable now, available in a moment — where this one never will be here, and a permanently disabled control teaches the reader that the row lies. What this gates is the interface, not the engine. dr-ingest takes storage traits and never a path, and cross-compiles to aarch64-linux-android today; it should need no changes when a SAF implementation lands. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,4 +15,4 @@ pub use storage::{
|
||||
DirRef, Entry, LocalStorage, NewFile, Node, SeekableRead, Storage, StorageError,
|
||||
WritableStorage,
|
||||
};
|
||||
pub use volumes::{volumes, Volume};
|
||||
pub use volumes::{imports_supported, volumes, Volume};
|
||||
|
||||
@@ -56,6 +56,32 @@ impl Volume {
|
||||
}
|
||||
}
|
||||
|
||||
/// TRACES: FR-CAT-10 | FR-PLAT-AND-1 | NFR-PORT-1
|
||||
/// Whether an import can reach a card on this platform at all.
|
||||
///
|
||||
/// **Not the same question as "did [`volumes`] find anything".** An empty list
|
||||
/// on Linux means no card is plugged in, and the user can plug one in or type
|
||||
/// where it is mounted. `false` here means the operation cannot be performed
|
||||
/// however hard the user tries, and the interface should not offer it.
|
||||
///
|
||||
/// It is `false` on Android, for two reasons that both have to be fixed before
|
||||
/// it can change:
|
||||
///
|
||||
/// - There is no mount table to read and no path to type. Storage is reached
|
||||
/// through a tree the user granted, and a removable volume appears there or
|
||||
/// not at all (ARCH §6.9).
|
||||
/// - Nothing implements [`WritableStorage`](crate::WritableStorage) except
|
||||
/// [`LocalStorage`](crate::LocalStorage), so there is no destination to write
|
||||
/// into even once a source is named.
|
||||
///
|
||||
/// The engine above this is already portable — it takes storage traits and
|
||||
/// never a path — so what this gates is the *interface*, and it stops being
|
||||
/// `false` when a SAF implementation lands rather than when the importer is
|
||||
/// rewritten.
|
||||
pub const fn imports_supported() -> bool {
|
||||
cfg!(target_os = "linux")
|
||||
}
|
||||
|
||||
/// Every mounted volume that might hold photographs.
|
||||
///
|
||||
/// Ordered with the likely cards first and, within each group, by label, so
|
||||
@@ -314,6 +340,17 @@ tmpfs /run/user/1000 tmpfs rw,nosuid 0 0
|
||||
assert!(!fixed.is_likely_card());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_platform_that_cannot_import_says_so_separately_from_finding_nothing() {
|
||||
// The two are different claims: an empty list means "plug one in",
|
||||
// `false` here means "this cannot be done here". An interface that
|
||||
// conflated them would offer a page that can never be used.
|
||||
assert_eq!(imports_supported(), cfg!(target_os = "linux"));
|
||||
if !imports_supported() {
|
||||
assert!(volumes().is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asking_where_there_is_no_answer_is_not_an_error() {
|
||||
// The call must work on a machine with no card, in CI, and on a
|
||||
|
||||
Reference in New Issue
Block a user