mirror of
https://github.com/storytold/lightcraft.git
synced 2026-11-02 06:27:53 -06:00
LRC-LIB-KEYWORDS: keyword list export names only what it loses
Export now leaves out only what it must, and its counts are exact: - A name starting with a byte-order mark is left out. On the first line, the reader would take the mark for the file's own. - A synonym in braces is written, since it reads back. An empty synonym, or one that repeats another in a different case, is skipped quietly, as the reader would drop it anyway. - keyword.export counts and checks for Capture One the keywords it actually wrote, now returned with the text. It used to guess from the lines, so a name starting with a brace was missed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a970bdac51
commit
060da5bb5e
@@ -434,20 +434,21 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"{path?} — the keyword list as a file (Lightroom Classic's format, which Capture One, Photo Supreme and Bridge read) → {keywords, captureOneRefuses: keywords with ; , < > that Capture One's importer refuses, unwritable: keywords left out because the format can't hold them (a name in brackets or braces, a line break, deeper than 64 levels; a synonym as \"path {synonym}\"), text when there is no path}",
|
||||
always,
|
||||
|s, p| {
|
||||
let (text, unwritable) = s.catalog.keyword_list_text();
|
||||
let keywords = text.lines().filter(|l| !l.trim_start().starts_with('{')).count();
|
||||
let refuses: Vec<String> = text
|
||||
.lines()
|
||||
.map(|l| l.trim().trim_start_matches('[').trim_end_matches(']').to_string())
|
||||
.filter(|l| !l.starts_with('{') && l.contains(CAPTURE_ONE_REFUSES))
|
||||
let file = s.catalog.keyword_list_text();
|
||||
// Capture One's importer refuses ; , < > in a keyword's name
|
||||
let refuses: Vec<&str> = file
|
||||
.written
|
||||
.iter()
|
||||
.filter_map(|p| p.rsplit(lightcraft_catalog::keywords::SEP).next())
|
||||
.filter(|n| n.contains(CAPTURE_ONE_REFUSES))
|
||||
.collect();
|
||||
let mut r = json!({"keywords": keywords, "captureOneRefuses": refuses, "unwritable": unwritable});
|
||||
let mut r = json!({"keywords": file.written.len(), "captureOneRefuses": refuses, "unwritable": file.unwritable});
|
||||
match str_param(p, "path").map(str::trim).filter(|x| !x.is_empty()) {
|
||||
Some(path) => {
|
||||
std::fs::write(path, &text).map_err(|e| crate::EngineError::Other(format!("{path}: {e}")))?;
|
||||
std::fs::write(path, &file.text).map_err(|e| crate::EngineError::Other(format!("{path}: {e}")))?;
|
||||
r["path"] = json!(path);
|
||||
}
|
||||
None => r["text"] = json!(text),
|
||||
None => r["text"] = json!(file.text),
|
||||
}
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
@@ -289,10 +289,15 @@ fn exporting_keywords_names_what_the_list_cant_hold() {
|
||||
let mut s = Session::with_demo();
|
||||
let id = s.visible_cloned()[0].0;
|
||||
s.execute("photo.setMeta", &json!({"ids": [id], "keywords": ["Travel|[draft]", "Travel|Lisbon"]})).unwrap();
|
||||
let before = s.execute("keyword.export", &json!({})).unwrap()["keywords"].as_u64().unwrap();
|
||||
// a name starting with a brace is written, counted and checked for Capture One like any other
|
||||
s.execute("photo.setMeta", &json!({"ids": [id], "addKeywords": ["{draft, notes"]})).unwrap();
|
||||
let r = s.execute("keyword.export", &json!({})).unwrap();
|
||||
assert_eq!(r["keywords"].as_u64(), Some(before + 1), "{r}");
|
||||
assert_eq!(r["captureOneRefuses"], json!(["{draft, notes"]), "{r}");
|
||||
assert_eq!(r["unwritable"], json!(["Travel|[draft]"]), "{r}");
|
||||
let text = r["text"].as_str().unwrap();
|
||||
assert!(text.contains("Travel\n\tLisbon\n") && !text.contains("draft"), "{text}");
|
||||
assert!(text.contains("Travel\n\tLisbon\n") && !text.contains("[draft]"), "{text}");
|
||||
s.execute("keyword.import", &json!({"text": text})).unwrap();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user