From 768f0d39a51267641c9e3edbc69bd17fee2f7c5f Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Wed, 29 Apr 2026 22:47:06 -0400 Subject: [PATCH] feat(core): add csv dep + import error variants Adds csv = "1" to relicario-core; introduces ImportCsvHeader and ImportCsvFormat. Foundation for the import_lastpass module landing in Task 2. Co-Authored-By: Claude Opus 4.7 --- Cargo.lock | 28 ++++++++++++++++++++++++++++ crates/relicario-core/Cargo.toml | 1 + crates/relicario-core/src/error.rs | 22 ++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 2d9d24e..19c769d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -437,6 +437,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -1609,6 +1630,7 @@ dependencies = [ "bip39", "chacha20poly1305", "chrono", + "csv", "ed25519-dalek", "getrandom 0.2.17", "hex", @@ -1696,6 +1718,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "same-file" version = "1.0.6" diff --git a/crates/relicario-core/Cargo.toml b/crates/relicario-core/Cargo.toml index 8456073..2eb1a32 100644 --- a/crates/relicario-core/Cargo.toml +++ b/crates/relicario-core/Cargo.toml @@ -29,5 +29,6 @@ getrandom = "0.2" zstd = { version = "0.13", default-features = false } tar = { version = "0.4", default-features = false } base64 = "0.22" +csv = "1" [dev-dependencies] diff --git a/crates/relicario-core/src/error.rs b/crates/relicario-core/src/error.rs index 5d75a82..8fb19bf 100644 --- a/crates/relicario-core/src/error.rs +++ b/crates/relicario-core/src/error.rs @@ -51,6 +51,17 @@ pub enum RelicarioError { #[error("backup envelope schema v{found}; this relicario reads v{expected}")] BackupSchemaMismatch { found: u32, expected: u32 }, + /// CSV header doesn't match the LastPass column layout. + #[error("unrecognized CSV header — expected LastPass export format ({0})")] + ImportCsvHeader(String), + + /// CSV body could not be parsed (mismatched quoting, encoding, etc.). + /// Per-row record errors that the importer recovers from become + /// `ImportWarning` entries — this variant is reserved for failures + /// that abort the whole import. + #[error("CSV parse failed: {0}")] + ImportCsvFormat(String), + /// An item was looked up by ID but does not exist in the manifest. #[error("item not found: {0}")] ItemNotFound(String), @@ -156,4 +167,15 @@ mod tests { let s = format!("{}", schema); assert!(s.contains("v2") && s.contains("v1")); } + + #[test] + fn import_errors_carry_useful_messages() { + let h = RelicarioError::ImportCsvHeader("missing 'name' column".into()); + assert!(format!("{}", h).contains("LastPass")); + assert!(format!("{}", h).contains("missing 'name'")); + + let f = RelicarioError::ImportCsvFormat("unterminated quote at line 12".into()); + assert!(format!("{}", f).contains("CSV parse failed")); + assert!(format!("{}", f).contains("unterminated quote")); + } }