stash/src/commands/import.rs
NotAShelf a41d72fb6b
stash: refactor error handling and entry deduplication
This includes breaking changes to the database entries, where we have
started deduplicating based on hashes instead of full entries. Entry
collisions are possible, but highly unlikely.

Additionally we use `Box<str>` for error variants to reduce allocations.
This is *yet* to give me a non-marginal performance benefit but doesn't
hurt to be more correct.

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I6a6a6964d0a33392da61372214ca3088551564ac
2025-09-19 14:08:15 +03:00

72 lines
1.8 KiB
Rust

use std::io::{self, BufRead};
use crate::db::{
ClipboardDb,
Entry,
SqliteClipboardDb,
StashError,
detect_mime,
};
pub trait ImportCommand {
/// Import clipboard entries from TSV format.
fn import_tsv(
&self,
input: impl io::Read,
max_items: u64,
) -> Result<(), StashError>;
}
impl ImportCommand for SqliteClipboardDb {
fn import_tsv(
&self,
input: impl io::Read,
max_items: u64,
) -> Result<(), StashError> {
let reader = io::BufReader::new(input);
let mut imported = 0;
for (lineno, line) in reader.lines().enumerate() {
let line = line.map_err(|e| {
StashError::Store(format!("Failed to read line {lineno}: {e}").into())
})?;
let mut parts = line.splitn(2, '\t');
let (Some(id_str), Some(val)) = (parts.next(), parts.next()) else {
return Err(StashError::Store(
format!("Malformed TSV line {lineno}: {line:?}").into(),
));
};
let Ok(_id) = id_str.parse::<u64>() else {
return Err(StashError::Store(
format!("Failed to parse id from line {lineno}: {id_str}").into(),
));
};
let entry = Entry {
contents: val.as_bytes().to_vec(),
mime: detect_mime(val.as_bytes()),
};
self
.conn
.execute(
"INSERT INTO clipboard (contents, mime) VALUES (?1, ?2)",
rusqlite::params![entry.contents, entry.mime],
)
.map_err(|e| {
StashError::Store(
format!("Failed to insert entry at line {lineno}: {e}").into(),
)
})?;
imported += 1;
}
log::info!("Imported {imported} records from TSV into SQLite database.");
// Trim database to max_items after import
self.trim_db(max_items)?;
log::info!("Trimmed clipboard database to max_items = {max_items}");
Ok(())
}
}