FR-CAT-13 asked for standard XMP and nothing in the tree parsed or wrote a byte of it. `keywords.rs` mentioned `dc:subject` in a comment about what a keyword's text is for, `dr-export`'s metadata module said "neither is read by `dr-decode` today" about its own half, and `dr-preset-xmp` reads a different file for a different requirement. So a library imported from Lightroom could come in and never go back out: a one-way door, which is not a thing a photographer walks their archive through. `core/dr-xmp` reads and writes the properties the requirement names — `dc:subject`, `lr:hierarchicalSubject`, `xmp:Rating`, `xmp:Label` and the IPTC core fields — from whichever shape the file happens to use. A property may arrive as an attribute or as an element, inside a Bag, a Seq, an Alt or no container at all, because the specification is not what wrote the file; so one collector takes whatever is in a property and the declared shape decides only how many values survive. `xmp:Rating="-1"` is modelled as Adobe's rejection rather than folded into zero stars, since DarkRoom keeps those on two axes and the mapping belongs where both are visible. Writing is a rewrite rather than a serialisation, and that is the whole design. An XMP sidecar is a shared document: the file beside a raw carries somebody else's `crs:` settings and comments and namespaces, and rendering our record over it would be data loss on every photograph but the first. The rule is stated once, in the crate documentation and in `PROPERTIES`: DarkRoom owns exactly those properties, identified by namespace URI and never by prefix, and nothing else in the document. Everything unowned is copied through byte for byte. A `Description` left empty once our properties come out of it is withdrawn, which is what keeps a rewrite idempotent instead of adding a husk to the file on every save. Precedence is settled conservatively, because a standard XMP carries no revision and no device and there is nothing in it to order two edits by. Keywords union, following the rule `dr_catalog::merge` already makes for assignments; every other field is taken only where DarkRoom holds none, following `Version::merge`'s judgement rule, and a genuine disagreement is reported rather than resolved so a caller can offer the reload the requirement asks for. What is deliberately left open — when a reload may happen without asking — is written down in the module rather than picked silently. No new dependency: quick-xml was already in the tree for WebDAV and for Lightroom presets. Nothing above the crate calls it yet, and `outstanding.md` now says so along with the two smaller gaps, GPS and the filename convention.
27 lines
1.2 KiB
TOML
27 lines
1.2 KiB
TOML
[package]
|
|
name = "dr-xmp"
|
|
version.workspace = true
|
|
edition.workspace = true
|
|
rust-version.workspace = true
|
|
license.workspace = true
|
|
repository.workspace = true
|
|
|
|
# No platform dependency and no filesystem, deliberately — the same rule
|
|
# `dr-export` states for the same reason. This crate turns *text* into a record
|
|
# and a record back into *text*; where those bytes come from and go is the
|
|
# caller's problem, because the answer differs by more than a path. On Linux it
|
|
# is a file beside the raw, on Android a SAF document with no path at all
|
|
# (ARCH §6.9), and on either it may be a `GET` and a `PUT` against Nextcloud.
|
|
[dependencies]
|
|
# `ColourLabel` alone. The mapping from `xmp:Label`'s text to the five labels
|
|
# the rest of the application already knows has to live somewhere, and anywhere
|
|
# else is a second table to keep in step with `dr_types::selector`.
|
|
dr-types.workspace = true
|
|
# XMP is real XML with namespaces and is not worth hand-rolling — the same
|
|
# sentence `dr-preset-xmp` writes, and for the same reason. Already in the tree
|
|
# for WebDAV and for Lightroom presets, so this is a use rather than a new
|
|
# dependency.
|
|
quick-xml.workspace = true
|
|
log.workspace = true
|
|
thiserror.workspace = true
|