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 <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-04-29 22:47:06 -04:00
parent b7180e70f9
commit 768f0d39a5
3 changed files with 51 additions and 0 deletions

28
Cargo.lock generated
View File

@@ -437,6 +437,27 @@ dependencies = [
"typenum", "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]] [[package]]
name = "curve25519-dalek" name = "curve25519-dalek"
version = "4.1.3" version = "4.1.3"
@@ -1609,6 +1630,7 @@ dependencies = [
"bip39", "bip39",
"chacha20poly1305", "chacha20poly1305",
"chrono", "chrono",
"csv",
"ed25519-dalek", "ed25519-dalek",
"getrandom 0.2.17", "getrandom 0.2.17",
"hex", "hex",
@@ -1696,6 +1718,12 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"

View File

@@ -29,5 +29,6 @@ getrandom = "0.2"
zstd = { version = "0.13", default-features = false } zstd = { version = "0.13", default-features = false }
tar = { version = "0.4", default-features = false } tar = { version = "0.4", default-features = false }
base64 = "0.22" base64 = "0.22"
csv = "1"
[dev-dependencies] [dev-dependencies]

View File

@@ -51,6 +51,17 @@ pub enum RelicarioError {
#[error("backup envelope schema v{found}; this relicario reads v{expected}")] #[error("backup envelope schema v{found}; this relicario reads v{expected}")]
BackupSchemaMismatch { found: u32, expected: u32 }, 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. /// An item was looked up by ID but does not exist in the manifest.
#[error("item not found: {0}")] #[error("item not found: {0}")]
ItemNotFound(String), ItemNotFound(String),
@@ -156,4 +167,15 @@ mod tests {
let s = format!("{}", schema); let s = format!("{}", schema);
assert!(s.contains("v2") && s.contains("v1")); 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"));
}
} }