refactor: reduce map_err boilerplate with context helper methods (TODO-025)
Add per-crate variant_ctx() helper methods to StorageError, BlobCacheError, ProtocolError, and BridgeError. Replace 78 .map_err(|e| format!(...)) closures with concise context calls. Identical error messages preserved.
This commit is contained in:
@@ -98,6 +98,12 @@ pub enum BridgeError {
|
|||||||
Unmapped(String),
|
Unmapped(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BridgeError {
|
||||||
|
fn unmapped_ctx(ctx: impl std::fmt::Display, e: impl std::fmt::Display) -> Self {
|
||||||
|
BridgeError::Unmapped(format!("{ctx}: {e}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<chanora_core::CoreError> for BridgeError {
|
impl From<chanora_core::CoreError> for BridgeError {
|
||||||
fn from(e: chanora_core::CoreError) -> Self {
|
fn from(e: chanora_core::CoreError) -> Self {
|
||||||
match e {
|
match e {
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ pub enum BlobCacheError {
|
|||||||
InvalidKey(String),
|
InvalidKey(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BlobCacheError {
|
||||||
|
fn io_ctx(ctx: impl std::fmt::Display, e: impl std::fmt::Display) -> Self {
|
||||||
|
BlobCacheError::Io(format!("{ctx}: {e}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Content-addressed blob cache backed by cacache.
|
/// Content-addressed blob cache backed by cacache.
|
||||||
pub struct BlobCache {
|
pub struct BlobCache {
|
||||||
cache_dir: PathBuf,
|
cache_dir: PathBuf,
|
||||||
@@ -51,7 +57,7 @@ impl BlobCache {
|
|||||||
// cacache creates the directory on first write, but we create
|
// cacache creates the directory on first write, but we create
|
||||||
// it eagerly so total_size() works before any writes.
|
// it eagerly so total_size() works before any writes.
|
||||||
std::fs::create_dir_all(&cache_dir)
|
std::fs::create_dir_all(&cache_dir)
|
||||||
.map_err(|e| BlobCacheError::Io(format!("mkdir cache: {e}")))?;
|
.map_err(|e| BlobCacheError::io_ctx("mkdir cache", e))?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
cache_dir,
|
cache_dir,
|
||||||
max_bytes,
|
max_bytes,
|
||||||
@@ -73,7 +79,7 @@ impl BlobCache {
|
|||||||
let cache_key = format!("{prefix}{key}");
|
let cache_key = format!("{prefix}{key}");
|
||||||
cacache::write(&self.cache_dir, &cache_key, data)
|
cacache::write(&self.cache_dir, &cache_key, data)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| BlobCacheError::Io(format!("cacache write: {e}")))?;
|
.map_err(|e| BlobCacheError::io_ctx("cacache write", e))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +112,7 @@ impl BlobCache {
|
|||||||
let cache_key = format!("{prefix}{key}");
|
let cache_key = format!("{prefix}{key}");
|
||||||
cacache::remove(&self.cache_dir, &cache_key)
|
cacache::remove(&self.cache_dir, &cache_key)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| BlobCacheError::Io(format!("cacache remove: {e}")))?;
|
.map_err(|e| BlobCacheError::io_ctx("cacache remove", e))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,14 +122,14 @@ impl BlobCache {
|
|||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
std::fs::remove_dir_all(&path)
|
std::fs::remove_dir_all(&path)
|
||||||
.map_err(|e| BlobCacheError::Io(format!("clear cache: {e}")))?;
|
.map_err(|e| BlobCacheError::io_ctx("clear cache", e))?;
|
||||||
std::fs::create_dir_all(&path)
|
std::fs::create_dir_all(&path)
|
||||||
.map_err(|e| BlobCacheError::Io(format!("recreate cache dir: {e}")))?;
|
.map_err(|e| BlobCacheError::io_ctx("recreate cache dir", e))?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|e| BlobCacheError::Io(format!("clear task: {e}")))?
|
.map_err(|e| BlobCacheError::io_ctx("clear task", e))?
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return total bytes used by all blobs.
|
/// Return total bytes used by all blobs.
|
||||||
@@ -148,7 +154,7 @@ impl BlobCache {
|
|||||||
Ok(total)
|
Ok(total)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|e| BlobCacheError::Io(format!("total_size task: {e}")))?
|
.map_err(|e| BlobCacheError::io_ctx("total_size task", e))?
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evict oldest entries by timestamp until total size is under
|
/// Evict oldest entries by timestamp until total size is under
|
||||||
@@ -198,7 +204,7 @@ impl BlobCache {
|
|||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|e| BlobCacheError::Io(format!("evict task: {e}")))?
|
.map_err(|e| BlobCacheError::io_ctx("evict task", e))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,3 +120,13 @@ pub enum ProtocolError {
|
|||||||
#[error("file transfer failed: {0}")]
|
#[error("file transfer failed: {0}")]
|
||||||
FileTransfer(String),
|
FileTransfer(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ProtocolError {
|
||||||
|
fn lost(msg: impl Into<String>) -> Self {
|
||||||
|
ProtocolError::Lost(msg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backend_ctx(ctx: impl std::fmt::Display, e: impl std::fmt::Display) -> Self {
|
||||||
|
ProtocolError::Backend(format!("{ctx}: {e}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,8 +79,26 @@ pub enum StorageError {
|
|||||||
Crypto(String),
|
Crypto(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StorageError {
|
||||||
|
fn io_ctx(ctx: impl std::fmt::Display, e: impl std::fmt::Display) -> Self {
|
||||||
|
StorageError::Io(format!("{ctx}: {e}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn crypto_ctx(ctx: impl std::fmt::Display, e: impl std::fmt::Display) -> Self {
|
||||||
|
StorageError::Crypto(format!("{ctx}: {e}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sqlite_ctx(ctx: impl std::fmt::Display, e: impl std::fmt::Display) -> Self {
|
||||||
|
StorageError::Sqlite(format!("{ctx}: {e}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn migration_ctx(ctx: impl std::fmt::Display, e: impl std::fmt::Display) -> Self {
|
||||||
|
StorageError::Migration(format!("{ctx}: {e}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn ensure_dir(dir: &Path) -> Result<(), StorageError> {
|
fn ensure_dir(dir: &Path) -> Result<(), StorageError> {
|
||||||
fs::create_dir_all(dir).map_err(|e| StorageError::Io(format!("mkdir {dir:?}: {e}")))
|
fs::create_dir_all(dir).map_err(|e| StorageError::io_ctx(format!("mkdir {dir:?}"), e))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Audio-related per-identity settings persisted alongside the
|
/// Audio-related per-identity settings persisted alongside the
|
||||||
@@ -246,7 +264,7 @@ impl IdentityFileStore {
|
|||||||
Ok(b64) => {
|
Ok(b64) => {
|
||||||
let bytes = base64::engine::general_purpose::STANDARD
|
let bytes = base64::engine::general_purpose::STANDARD
|
||||||
.decode(b64.as_bytes())
|
.decode(b64.as_bytes())
|
||||||
.map_err(|e| StorageError::Crypto(format!("keyring dek decode: {e}")))?;
|
.map_err(|e| StorageError::crypto_ctx("keyring dek decode", e))?;
|
||||||
if bytes.len() != 32 {
|
if bytes.len() != 32 {
|
||||||
return Err(StorageError::Crypto(format!(
|
return Err(StorageError::Crypto(format!(
|
||||||
"keyring dek length {} (expected 32)",
|
"keyring dek length {} (expected 32)",
|
||||||
@@ -359,9 +377,9 @@ impl IdentityFileStore {
|
|||||||
let _ = self.keyring_save(&key);
|
let _ = self.keyring_save(&key);
|
||||||
let mut f = open_private(&self.dek_path)?;
|
let mut f = open_private(&self.dek_path)?;
|
||||||
f.write_all(&key)
|
f.write_all(&key)
|
||||||
.map_err(|e| StorageError::Io(format!("write dek: {e}")))?;
|
.map_err(|e| StorageError::io_ctx("write dek", e))?;
|
||||||
f.sync_all()
|
f.sync_all()
|
||||||
.map_err(|e| StorageError::Io(format!("sync dek: {e}")))?;
|
.map_err(|e| StorageError::io_ctx("sync dek", e))?;
|
||||||
info!(target: "chanora_storage", path = ?self.dek_path, "DEK generated (file fallback)");
|
info!(target: "chanora_storage", path = ?self.dek_path, "DEK generated (file fallback)");
|
||||||
key.zeroize();
|
key.zeroize();
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -397,11 +415,11 @@ impl IdentityFileStore {
|
|||||||
let mut f = match fs::File::open(&self.path) {
|
let mut f = match fs::File::open(&self.path) {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),
|
||||||
Err(e) => return Err(StorageError::Io(format!("open {:?}: {e}", self.path))),
|
Err(e) => return Err(StorageError::io_ctx(format!("open {:?}", self.path), e)),
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
f.read_to_end(&mut buf)
|
f.read_to_end(&mut buf)
|
||||||
.map_err(|e| StorageError::Io(format!("read {:?}: {e}", self.path)))?;
|
.map_err(|e| StorageError::io_ctx(format!("read {:?}", self.path), e))?;
|
||||||
if buf.is_empty() {
|
if buf.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
@@ -420,11 +438,11 @@ impl IdentityFileStore {
|
|||||||
let nonce = Nonce::from_slice(nonce_bytes);
|
let nonce = Nonce::from_slice(nonce_bytes);
|
||||||
let pt = cipher.decrypt(nonce, &buf[12..]).map_err(|e| {
|
let pt = cipher.decrypt(nonce, &buf[12..]).map_err(|e| {
|
||||||
key_bytes.zeroize();
|
key_bytes.zeroize();
|
||||||
StorageError::Crypto(format!("decrypt: {e}"))
|
StorageError::crypto_ctx("decrypt", e)
|
||||||
})?;
|
})?;
|
||||||
key_bytes.zeroize();
|
key_bytes.zeroize();
|
||||||
let s = String::from_utf8(pt)
|
let s = String::from_utf8(pt)
|
||||||
.map_err(|e| StorageError::Crypto(format!("plaintext not utf8: {e}")))?;
|
.map_err(|e| StorageError::crypto_ctx("plaintext not utf8", e))?;
|
||||||
let trimmed = s.trim().to_string();
|
let trimmed = s.trim().to_string();
|
||||||
if trimmed.is_empty() {
|
if trimmed.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@@ -440,7 +458,7 @@ impl IdentityFileStore {
|
|||||||
"identity file is in legacy plaintext format; will encrypt on next save"
|
"identity file is in legacy plaintext format; will encrypt on next save"
|
||||||
);
|
);
|
||||||
let s = String::from_utf8(buf)
|
let s = String::from_utf8(buf)
|
||||||
.map_err(|e| StorageError::Io(format!("legacy not utf8: {e}")))?;
|
.map_err(|e| StorageError::io_ctx("legacy not utf8", e))?;
|
||||||
let trimmed = s.trim().to_string();
|
let trimmed = s.trim().to_string();
|
||||||
if trimmed.is_empty() {
|
if trimmed.is_empty() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@@ -462,7 +480,7 @@ impl IdentityFileStore {
|
|||||||
let nonce = Nonce::from_slice(&nonce_bytes);
|
let nonce = Nonce::from_slice(&nonce_bytes);
|
||||||
let ct = cipher.encrypt(nonce, plaintext).map_err(|e| {
|
let ct = cipher.encrypt(nonce, plaintext).map_err(|e| {
|
||||||
key_bytes.zeroize();
|
key_bytes.zeroize();
|
||||||
StorageError::Crypto(format!("encrypt: {e}"))
|
StorageError::crypto_ctx("encrypt", e)
|
||||||
})?;
|
})?;
|
||||||
key_bytes.zeroize();
|
key_bytes.zeroize();
|
||||||
|
|
||||||
@@ -471,14 +489,14 @@ impl IdentityFileStore {
|
|||||||
{
|
{
|
||||||
let mut f = open_private(&tmp)?;
|
let mut f = open_private(&tmp)?;
|
||||||
f.write_all(&nonce_bytes)
|
f.write_all(&nonce_bytes)
|
||||||
.map_err(|e| StorageError::Io(format!("write nonce: {e}")))?;
|
.map_err(|e| StorageError::io_ctx("write nonce", e))?;
|
||||||
f.write_all(&ct)
|
f.write_all(&ct)
|
||||||
.map_err(|e| StorageError::Io(format!("write ct: {e}")))?;
|
.map_err(|e| StorageError::io_ctx("write ct", e))?;
|
||||||
f.sync_all()
|
f.sync_all()
|
||||||
.map_err(|e| StorageError::Io(format!("sync {tmp:?}: {e}")))?;
|
.map_err(|e| StorageError::io_ctx(format!("sync {tmp:?}"), e))?;
|
||||||
}
|
}
|
||||||
fs::rename(&tmp, &self.path)
|
fs::rename(&tmp, &self.path)
|
||||||
.map_err(|e| StorageError::Io(format!("rename {tmp:?} -> {:?}: {e}", self.path)))?;
|
.map_err(|e| StorageError::io_ctx(format!("rename {tmp:?} -> {:?}", self.path), e))?;
|
||||||
info!(target: "chanora_storage", path = ?self.path, "identity persisted (encrypted)");
|
info!(target: "chanora_storage", path = ?self.path, "identity persisted (encrypted)");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -507,17 +525,17 @@ impl IdentityFileStore {
|
|||||||
let path = self.meta_path();
|
let path = self.meta_path();
|
||||||
let tmp = path.with_extension("json.tmp");
|
let tmp = path.with_extension("json.tmp");
|
||||||
let body = serde_json::to_vec_pretty(m)
|
let body = serde_json::to_vec_pretty(m)
|
||||||
.map_err(|e| StorageError::Io(format!("meta serialize: {e}")))?;
|
.map_err(|e| StorageError::io_ctx("meta serialize", e))?;
|
||||||
{
|
{
|
||||||
let mut f = fs::File::create(&tmp)
|
let mut f = fs::File::create(&tmp)
|
||||||
.map_err(|e| StorageError::Io(format!("open meta {tmp:?}: {e}")))?;
|
.map_err(|e| StorageError::io_ctx(format!("open meta {tmp:?}"), e))?;
|
||||||
f.write_all(&body)
|
f.write_all(&body)
|
||||||
.map_err(|e| StorageError::Io(format!("write meta: {e}")))?;
|
.map_err(|e| StorageError::io_ctx("write meta", e))?;
|
||||||
f.sync_all()
|
f.sync_all()
|
||||||
.map_err(|e| StorageError::Io(format!("sync meta: {e}")))?;
|
.map_err(|e| StorageError::io_ctx("sync meta", e))?;
|
||||||
}
|
}
|
||||||
fs::rename(&tmp, &path)
|
fs::rename(&tmp, &path)
|
||||||
.map_err(|e| StorageError::Io(format!("rename meta {tmp:?} -> {path:?}: {e}")))?;
|
.map_err(|e| StorageError::io_ctx(format!("rename meta {tmp:?} -> {path:?}"), e))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,10 +641,10 @@ fn is_plausibly_legacy_plaintext(buf: &[u8]) -> bool {
|
|||||||
/// and the legacy migration path inside `ensure_dek`.
|
/// and the legacy migration path inside `ensure_dek`.
|
||||||
fn read_file_dek(path: &Path) -> Result<[u8; 32], StorageError> {
|
fn read_file_dek(path: &Path) -> Result<[u8; 32], StorageError> {
|
||||||
let mut f =
|
let mut f =
|
||||||
fs::File::open(path).map_err(|e| StorageError::Io(format!("open dek {:?}: {e}", path)))?;
|
fs::File::open(path).map_err(|e| StorageError::io_ctx(format!("open dek {path:?}"), e))?;
|
||||||
let mut key = [0u8; 32];
|
let mut key = [0u8; 32];
|
||||||
f.read_exact(&mut key)
|
f.read_exact(&mut key)
|
||||||
.map_err(|e| StorageError::Io(format!("read dek: {e}")))?;
|
.map_err(|e| StorageError::io_ctx("read dek", e))?;
|
||||||
Ok(key)
|
Ok(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,7 +674,7 @@ fn open_private(p: &Path) -> Result<fs::File, StorageError> {
|
|||||||
.truncate(true)
|
.truncate(true)
|
||||||
.mode(0o600)
|
.mode(0o600)
|
||||||
.open(p)
|
.open(p)
|
||||||
.map_err(|e| StorageError::Io(format!("open {p:?}: {e}")))
|
.map_err(|e| StorageError::io_ctx(format!("open {p:?}"), e))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
@@ -670,7 +688,7 @@ fn open_private(p: &Path) -> Result<fs::File, StorageError> {
|
|||||||
.write(true)
|
.write(true)
|
||||||
.truncate(true)
|
.truncate(true)
|
||||||
.open(p)
|
.open(p)
|
||||||
.map_err(|e| StorageError::Io(format!("open {p:?}: {e}")))
|
.map_err(|e| StorageError::io_ctx(format!("open {p:?}"), e))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Public abstraction over the per-install envelope-encryption
|
/// Public abstraction over the per-install envelope-encryption
|
||||||
@@ -725,7 +743,7 @@ impl DekCrypto {
|
|||||||
let nonce = Nonce::from_slice(&nonce_bytes);
|
let nonce = Nonce::from_slice(&nonce_bytes);
|
||||||
let ct = cipher
|
let ct = cipher
|
||||||
.encrypt(nonce, plaintext)
|
.encrypt(nonce, plaintext)
|
||||||
.map_err(|e| StorageError::Crypto(format!("encrypt: {e}")))?;
|
.map_err(|e| StorageError::crypto_ctx("encrypt", e))?;
|
||||||
let mut out = Vec::with_capacity(12 + ct.len());
|
let mut out = Vec::with_capacity(12 + ct.len());
|
||||||
out.extend_from_slice(&nonce_bytes);
|
out.extend_from_slice(&nonce_bytes);
|
||||||
out.extend_from_slice(&ct);
|
out.extend_from_slice(&ct);
|
||||||
@@ -744,7 +762,7 @@ impl DekCrypto {
|
|||||||
let nonce = Nonce::from_slice(&blob[..12]);
|
let nonce = Nonce::from_slice(&blob[..12]);
|
||||||
cipher
|
cipher
|
||||||
.decrypt(nonce, &blob[12..])
|
.decrypt(nonce, &blob[12..])
|
||||||
.map_err(|e| StorageError::Crypto(format!("decrypt: {e}")))
|
.map_err(|e| StorageError::crypto_ctx("decrypt", e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -821,9 +839,9 @@ impl BookmarkRepository {
|
|||||||
ensure_dir(dir)?;
|
ensure_dir(dir)?;
|
||||||
let path = dir.join("chanora.db");
|
let path = dir.join("chanora.db");
|
||||||
let conn = Connection::open(&path)
|
let conn = Connection::open(&path)
|
||||||
.map_err(|e| StorageError::Sqlite(format!("open {path:?}: {e}")))?;
|
.map_err(|e| StorageError::sqlite_ctx(format!("open {path:?}"), e))?;
|
||||||
conn.pragma_update(None, "foreign_keys", "ON")
|
conn.pragma_update(None, "foreign_keys", "ON")
|
||||||
.map_err(|e| StorageError::Sqlite(format!("pragma: {e}")))?;
|
.map_err(|e| StorageError::sqlite_ctx("pragma", e))?;
|
||||||
conn.execute_batch(
|
conn.execute_batch(
|
||||||
"CREATE TABLE IF NOT EXISTS bookmarks (
|
"CREATE TABLE IF NOT EXISTS bookmarks (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
@@ -837,7 +855,7 @@ impl BookmarkRepository {
|
|||||||
);
|
);
|
||||||
INSERT OR IGNORE INTO schema_version(v) VALUES (1);",
|
INSERT OR IGNORE INTO schema_version(v) VALUES (1);",
|
||||||
)
|
)
|
||||||
.map_err(|e| StorageError::Migration(format!("init schema: {e}")))?;
|
.map_err(|e| StorageError::migration_ctx("init schema", e))?;
|
||||||
// Schema v2 migration: encrypted password column. Idempotent.
|
// Schema v2 migration: encrypted password column. Idempotent.
|
||||||
let has_blob: i64 = conn
|
let has_blob: i64 = conn
|
||||||
.query_row(
|
.query_row(
|
||||||
@@ -845,12 +863,12 @@ impl BookmarkRepository {
|
|||||||
[],
|
[],
|
||||||
|r| r.get(0),
|
|r| r.get(0),
|
||||||
)
|
)
|
||||||
.map_err(|e| StorageError::Migration(format!("table_info: {e}")))?;
|
.map_err(|e| StorageError::migration_ctx("table_info", e))?;
|
||||||
if has_blob == 0 {
|
if has_blob == 0 {
|
||||||
conn.execute("ALTER TABLE bookmarks ADD COLUMN password_blob BLOB", [])
|
conn.execute("ALTER TABLE bookmarks ADD COLUMN password_blob BLOB", [])
|
||||||
.map_err(|e| StorageError::Migration(format!("add password_blob: {e}")))?;
|
.map_err(|e| StorageError::migration_ctx("add password_blob", e))?;
|
||||||
conn.execute("INSERT OR IGNORE INTO schema_version(v) VALUES (2)", [])
|
conn.execute("INSERT OR IGNORE INTO schema_version(v) VALUES (2)", [])
|
||||||
.map_err(|e| StorageError::Migration(format!("bump version: {e}")))?;
|
.map_err(|e| StorageError::migration_ctx("bump version", e))?;
|
||||||
info!(target: "chanora_storage", "bookmark db migrated to v2 (password_blob)");
|
info!(target: "chanora_storage", "bookmark db migrated to v2 (password_blob)");
|
||||||
}
|
}
|
||||||
info!(target: "chanora_storage", path = ?path, "bookmark db opened");
|
info!(target: "chanora_storage", path = ?path, "bookmark db opened");
|
||||||
@@ -886,7 +904,7 @@ impl BookmarkRepository {
|
|||||||
"INSERT INTO bookmarks (display_name, host, nickname, password, password_blob) VALUES (?1, ?2, ?3, ?4, ?5)",
|
"INSERT INTO bookmarks (display_name, host, nickname, password, password_blob) VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||||
params![b.display_name, b.host, b.nickname, plain, blob],
|
params![b.display_name, b.host, b.nickname, plain, blob],
|
||||||
)
|
)
|
||||||
.map_err(|e| StorageError::Sqlite(format!("insert: {e}")))?;
|
.map_err(|e| StorageError::sqlite_ctx("insert", e))?;
|
||||||
Ok(conn.last_insert_rowid())
|
Ok(conn.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -915,20 +933,20 @@ impl BookmarkRepository {
|
|||||||
|row| row.get(0),
|
|row| row.get(0),
|
||||||
)
|
)
|
||||||
.optional()
|
.optional()
|
||||||
.map_err(|e| StorageError::Sqlite(format!("select: {e}")))?;
|
.map_err(|e| StorageError::sqlite_ctx("select", e))?;
|
||||||
if let Some(id) = existing {
|
if let Some(id) = existing {
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"UPDATE bookmarks SET nickname = ?1, password = ?2, password_blob = ?3 WHERE id = ?4",
|
"UPDATE bookmarks SET nickname = ?1, password = ?2, password_blob = ?3 WHERE id = ?4",
|
||||||
params![b.nickname, plain, blob, id],
|
params![b.nickname, plain, blob, id],
|
||||||
)
|
)
|
||||||
.map_err(|e| StorageError::Sqlite(format!("update: {e}")))?;
|
.map_err(|e| StorageError::sqlite_ctx("update", e))?;
|
||||||
Ok(id)
|
Ok(id)
|
||||||
} else {
|
} else {
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO bookmarks (display_name, host, nickname, password, password_blob) VALUES (?1, ?2, ?3, ?4, ?5)",
|
"INSERT INTO bookmarks (display_name, host, nickname, password, password_blob) VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||||
params![b.display_name, b.host, b.nickname, plain, blob],
|
params![b.display_name, b.host, b.nickname, plain, blob],
|
||||||
)
|
)
|
||||||
.map_err(|e| StorageError::Sqlite(format!("insert: {e}")))?;
|
.map_err(|e| StorageError::sqlite_ctx("insert", e))?;
|
||||||
Ok(conn.last_insert_rowid())
|
Ok(conn.last_insert_rowid())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -957,7 +975,7 @@ impl BookmarkRepository {
|
|||||||
"UPDATE bookmarks SET display_name=?1, host=?2, nickname=?3, password=?4, password_blob=?5 WHERE id=?6",
|
"UPDATE bookmarks SET display_name=?1, host=?2, nickname=?3, password=?4, password_blob=?5 WHERE id=?6",
|
||||||
params![b.display_name, b.host, b.nickname, plain, blob, b.id],
|
params![b.display_name, b.host, b.nickname, plain, blob, b.id],
|
||||||
)
|
)
|
||||||
.map_err(|e| StorageError::Sqlite(format!("update: {e}")))?;
|
.map_err(|e| StorageError::sqlite_ctx("update", e))?;
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
Err(StorageError::NotFound)
|
Err(StorageError::NotFound)
|
||||||
} else {
|
} else {
|
||||||
@@ -972,7 +990,7 @@ impl BookmarkRepository {
|
|||||||
.lock()
|
.lock()
|
||||||
.map_err(|_| StorageError::Sqlite("poisoned lock".to_string()))?;
|
.map_err(|_| StorageError::Sqlite("poisoned lock".to_string()))?;
|
||||||
conn.execute("DELETE FROM bookmarks WHERE id = ?1", params![id])
|
conn.execute("DELETE FROM bookmarks WHERE id = ?1", params![id])
|
||||||
.map_err(|e| StorageError::Sqlite(format!("delete: {e}")))?;
|
.map_err(|e| StorageError::sqlite_ctx("delete", e))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -988,7 +1006,7 @@ impl BookmarkRepository {
|
|||||||
.prepare(
|
.prepare(
|
||||||
"SELECT id, display_name, host, nickname, password, password_blob FROM bookmarks ORDER BY id",
|
"SELECT id, display_name, host, nickname, password, password_blob FROM bookmarks ORDER BY id",
|
||||||
)
|
)
|
||||||
.map_err(|e| StorageError::Sqlite(format!("prepare: {e}")))?;
|
.map_err(|e| StorageError::sqlite_ctx("prepare", e))?;
|
||||||
let rows = stmt
|
let rows = stmt
|
||||||
.query_map([], |row| {
|
.query_map([], |row| {
|
||||||
let id: i64 = row.get(0)?;
|
let id: i64 = row.get(0)?;
|
||||||
@@ -999,15 +1017,15 @@ impl BookmarkRepository {
|
|||||||
let blob: Option<Vec<u8>> = row.get(5)?;
|
let blob: Option<Vec<u8>> = row.get(5)?;
|
||||||
Ok((id, display_name, host, nickname, plain, blob))
|
Ok((id, display_name, host, nickname, plain, blob))
|
||||||
})
|
})
|
||||||
.map_err(|e| StorageError::Sqlite(format!("query: {e}")))?;
|
.map_err(|e| StorageError::sqlite_ctx("query", e))?;
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
for r in rows {
|
for r in rows {
|
||||||
let (id, display_name, host, nickname, plain, blob) =
|
let (id, display_name, host, nickname, plain, blob) =
|
||||||
r.map_err(|e| StorageError::Sqlite(format!("row: {e}")))?;
|
r.map_err(|e| StorageError::sqlite_ctx("row", e))?;
|
||||||
let password = match (blob.as_ref(), self.crypto.as_ref()) {
|
let password = match (blob.as_ref(), self.crypto.as_ref()) {
|
||||||
(Some(b), Some(c)) => Some(
|
(Some(b), Some(c)) => Some(
|
||||||
String::from_utf8(c.decrypt(b)?)
|
String::from_utf8(c.decrypt(b)?)
|
||||||
.map_err(|e| StorageError::Crypto(format!("blob utf8: {e}")))?,
|
.map_err(|e| StorageError::crypto_ctx("blob utf8", e))?,
|
||||||
),
|
),
|
||||||
(Some(_), None) => {
|
(Some(_), None) => {
|
||||||
// We have an encrypted blob but no key. Skip the
|
// We have an encrypted blob but no key. Skip the
|
||||||
|
|||||||
Reference in New Issue
Block a user