wip: ingest
This commit is contained in:
@@ -17,7 +17,7 @@ pub use colour::{Chromaticities, Transfer};
|
||||
pub use selector::{ColourLabel, DateSelector, FlagState, Selector, Tier};
|
||||
pub use settings::{
|
||||
CacheSettings, CollisionPolicy, ColourSpace, DevelopSettings, ExportFormat, ExportSettings,
|
||||
ExportTarget, OutputSharpening, Settings, SizingMode,
|
||||
ExportTarget, ImportSettings, OutputSharpening, Settings, SizingMode,
|
||||
};
|
||||
pub use time::{civil_from_unix, civil_from_unix_at, format_date, Civil};
|
||||
|
||||
|
||||
@@ -52,6 +52,72 @@ pub struct Settings {
|
||||
pub cache: CacheSettings,
|
||||
pub export: ExportSettings,
|
||||
pub develop: DevelopSettings,
|
||||
pub import: ImportSettings,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Import
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// TRACES: FR-CAT-10 | FR-NC-7a
|
||||
/// What an import defaults to, remembered between cards.
|
||||
///
|
||||
/// A photographer imports the same way every time — same folders, same
|
||||
/// destination, same answer about the card — and retyping a template on every
|
||||
/// card is the kind of friction that makes people stop using the importer.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct ImportSettings {
|
||||
/// The folder layout (FR-NC-7a).
|
||||
///
|
||||
/// **Empty means the layout module's default**, rather than this file
|
||||
/// repeating `{yyyy}/{yyyy}-{mm}-{dd}`. Two spellings of a default is one
|
||||
/// that goes stale, and the expander is where the token vocabulary is
|
||||
/// defined — so it owns the default too, and this records only a
|
||||
/// deliberate departure from it.
|
||||
pub folder_template: String,
|
||||
/// Where originals are copied to on this machine. Empty until chosen.
|
||||
///
|
||||
/// A local folder even on a library that lives on a server: FR-NC-7b has
|
||||
/// the bytes land and be verified here first, and the upload follow.
|
||||
pub destination: String,
|
||||
/// The optional second copy (FR-CAT-10). Empty means none.
|
||||
pub backup: String,
|
||||
/// Whether to send imported originals on to the server (FR-NC-7b).
|
||||
pub upload: bool,
|
||||
/// Whether the card is to be emptied once everything is confirmed.
|
||||
///
|
||||
/// **Defaults to false and is the one setting worth not remembering
|
||||
/// eagerly.** A move-import is the only irreversible thing this
|
||||
/// application does, and a preference that quietly persists "yes, erase
|
||||
/// the card" across sessions turns a considered decision into a default.
|
||||
pub move_from_card: bool,
|
||||
/// Whether each copy is read back and compared (FR-CAT-10).
|
||||
pub verify: bool,
|
||||
/// Whether photographs already in the catalog are skipped (FR-CAT-11).
|
||||
pub skip_duplicates: bool,
|
||||
}
|
||||
|
||||
/// Written out rather than derived, because three of these must not be
|
||||
/// `false`.
|
||||
///
|
||||
/// `#[serde(default)]` uses this for a field a stored file is missing *and*
|
||||
/// for the whole section when a settings file predates it — which every
|
||||
/// existing file does. A derived `Default` would silently turn verification
|
||||
/// off and duplicate skipping off for every user who already has one.
|
||||
impl Default for ImportSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
folder_template: String::new(),
|
||||
destination: String::new(),
|
||||
backup: String::new(),
|
||||
upload: true,
|
||||
// The one irreversible thing this application does starts off.
|
||||
move_from_card: false,
|
||||
verify: true,
|
||||
skip_duplicates: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -1014,3 +1080,41 @@ mod tests {
|
||||
assert_eq!(budget::label(None), "Unlimited");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod import_settings_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn an_existing_settings_file_gains_the_safe_defaults() {
|
||||
// Every settings file on disk predates this section. Coming back with
|
||||
// verification off would silently remove the check that catches a
|
||||
// dying card, on precisely the machines that have been running
|
||||
// longest.
|
||||
let stored = r#"{"cache":{},"export":{},"develop":{}}"#;
|
||||
let s: Settings = serde_json::from_str(stored).unwrap();
|
||||
assert!(s.import.verify);
|
||||
assert!(s.import.skip_duplicates);
|
||||
assert!(s.import.upload);
|
||||
// And the irreversible one stays off.
|
||||
assert!(!s.import.move_from_card);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_unset_template_defers_to_the_layout_rather_than_repeating_it() {
|
||||
// Empty is meaningful: it means "whatever the expander's default is",
|
||||
// so the default lives in exactly one place.
|
||||
assert_eq!(Settings::default().import.folder_template, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_deliberate_choice_survives_a_round_trip() {
|
||||
let mut s = Settings::default();
|
||||
s.import.folder_template = "{yyyy}/{make}".into();
|
||||
s.import.move_from_card = true;
|
||||
s.import.verify = false;
|
||||
let text = serde_json::to_string(&s).unwrap();
|
||||
let back: Settings = serde_json::from_str(&text).unwrap();
|
||||
assert_eq!(back.import, s.import);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user