feat(core): import_lastpass — group, favorite, notes
Map LastPass grouping/fav/extra columns to relicario item metadata. Grouping becomes item.group, fav="1" sets item.favorite, extra becomes item.notes. Multi-line extra via CSV quoting round-trips correctly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -94,8 +94,8 @@ fn map_row(record: &csv::StringRecord, row: usize) -> std::result::Result<Item,
|
|||||||
let _totp = record.get(3).unwrap_or(""); // populated in Task 4
|
let _totp = record.get(3).unwrap_or(""); // populated in Task 4
|
||||||
let extra = record.get(4).unwrap_or("");
|
let extra = record.get(4).unwrap_or("");
|
||||||
let name = record.get(5).unwrap_or("").trim();
|
let name = record.get(5).unwrap_or("").trim();
|
||||||
let _group = record.get(6).unwrap_or("").trim(); // populated in Task 3
|
let group = record.get(6).unwrap_or("").trim();
|
||||||
let _fav = record.get(7).unwrap_or("").trim(); // populated in Task 3
|
let fav = record.get(7).unwrap_or("").trim();
|
||||||
|
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
return Err(ImportWarning {
|
return Err(ImportWarning {
|
||||||
@@ -124,6 +124,8 @@ fn map_row(record: &csv::StringRecord, row: usize) -> std::result::Result<Item,
|
|||||||
totp: None,
|
totp: None,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
item.notes = if extra.is_empty() { None } else { Some(extra.to_string()) };
|
item.group = if group.is_empty() { None } else { Some(group.to_string()) };
|
||||||
|
item.favorite = fav == "1";
|
||||||
|
item.notes = if extra.is_empty() { None } else { Some(extra.to_string()) };
|
||||||
Ok(item)
|
Ok(item)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,3 +44,57 @@ fn item_id_is_freshly_minted() {
|
|||||||
fn first_warning_message(warnings: &[ImportWarning]) -> String {
|
fn first_warning_message(warnings: &[ImportWarning]) -> String {
|
||||||
warnings.first().expect("expected at least one warning").message.clone()
|
warnings.first().expect("expected at least one warning").message.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grouping_maps_to_item_group() {
|
||||||
|
let csv = format!("{HEADER}\nhttps://x,u,p,,,Bank,Finance,");
|
||||||
|
let (items, warnings) = parse_lastpass_csv(csv.as_bytes()).unwrap();
|
||||||
|
assert!(warnings.is_empty());
|
||||||
|
assert_eq!(items[0].group.as_deref(), Some("Finance"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_grouping_yields_none() {
|
||||||
|
let csv = format!("{HEADER}\nhttps://x,u,p,,,Bank,,");
|
||||||
|
let (items, _) = parse_lastpass_csv(csv.as_bytes()).unwrap();
|
||||||
|
assert!(items[0].group.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fav_one_marks_favorite() {
|
||||||
|
let csv = format!("{HEADER}\nhttps://x,u,p,,,Bank,,1");
|
||||||
|
let (items, _) = parse_lastpass_csv(csv.as_bytes()).unwrap();
|
||||||
|
assert!(items[0].favorite);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fav_zero_or_blank_not_favorite() {
|
||||||
|
let csv = format!(
|
||||||
|
"{HEADER}\n\
|
||||||
|
https://x,u,p,,,Zero,,0\n\
|
||||||
|
https://x,u,p,,,Blank,,",
|
||||||
|
);
|
||||||
|
let (items, _) = parse_lastpass_csv(csv.as_bytes()).unwrap();
|
||||||
|
assert_eq!(items.len(), 2);
|
||||||
|
assert!(!items[0].favorite);
|
||||||
|
assert!(!items[1].favorite);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extra_becomes_notes_for_login() {
|
||||||
|
let csv = format!("{HEADER}\nhttps://x,u,p,,a hint,Bank,,");
|
||||||
|
let (items, _) = parse_lastpass_csv(csv.as_bytes()).unwrap();
|
||||||
|
assert_eq!(items[0].notes.as_deref(), Some("a hint"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiline_extra_round_trips_via_quoting() {
|
||||||
|
// CSV double-quotes escape embedded newlines.
|
||||||
|
let csv = format!(
|
||||||
|
"{HEADER}\n\
|
||||||
|
https://x,u,p,,\"line1\nline2\nline3\",Bank,,",
|
||||||
|
);
|
||||||
|
let (items, warnings) = parse_lastpass_csv(csv.as_bytes()).unwrap();
|
||||||
|
assert!(warnings.is_empty(), "multi-line extra should parse cleanly");
|
||||||
|
assert_eq!(items[0].notes.as_deref(), Some("line1\nline2\nline3"));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user