diff --git a/Cargo.toml b/Cargo.toml index 3ee1cf1..4055883 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,11 +21,15 @@ exclude = [ anyhow = "1" async-trait = "0.1" chrono = { version = "0.4", features = ["serde"] } -git2 = "0.19" +# Native-linking deps pinned to OpenHuman's versions so the host and the crate +# link ONE bundled SQLite and ONE libgit2 (both are `links = "…"` crates; two +# versions in one binary is a hard Cargo error). Keep in lockstep with the host +# root Cargo.toml when either side bumps. +git2 = { version = "0.21", default-features = false, features = ["vendored-libgit2"] } parking_lot = "0.12" rand = "0.8" regex = "1" -rusqlite = { version = "0.32", features = ["bundled"] } +rusqlite = { version = "0.40", features = ["bundled"] } serde = { version = "1", features = ["derive"] } serde_json = "1" sha2 = "0.10" diff --git a/src/memory/diff/ledger.rs b/src/memory/diff/ledger.rs index 468d40a..7b8760c 100644 --- a/src/memory/diff/ledger.rs +++ b/src/memory/diff/ledger.rs @@ -366,7 +366,9 @@ impl Ledger { }; Ok(Some(checkpoint_from_message( checkpoint_id, - tag.message().unwrap_or(""), + // git2 0.21: Tag::message() is Result, _>; a non-UTF8 + // or missing message degrades to an empty checkpoint body. + tag.message().ok().flatten().unwrap_or(""), ))) } @@ -375,8 +377,9 @@ impl Ledger { let pattern = format!("{CHECKPOINT_PREFIX}*"); let names = self.repo.tag_names(Some(&pattern))?; let mut out = Vec::new(); - // StringArray::iter() yields Option<&str>; drop non-utf8 names. - for name in names.iter().flatten() { + // git2 0.21: StringArray::iter() yields Result, _>; keep + // only successfully-decoded utf8 names. + for name in names.iter().filter_map(|r| r.ok().flatten()) { if let Some(ckpt) = self.get_checkpoint(name)? { out.push(ckpt); } @@ -394,7 +397,8 @@ impl Ledger { let pattern = format!("{CHECKPOINT_PREFIX}*"); let names = self.repo.tag_names(Some(&pattern))?; let mut deleted = 0u64; - for name in names.iter().flatten() { + // git2 0.21: StringArray::iter() yields Result, _>. + for name in names.iter().filter_map(|r| r.ok().flatten()) { if let Some(ckpt) = self.get_checkpoint(name)? { if ckpt.created_at_ms < older_than_ms { self.repo.tag_delete(name)?; @@ -545,7 +549,8 @@ fn oid_hash(oid: Oid) -> Option { fn patch_text(diff: &git2::Diff, delta_idx: usize) -> Option { let mut patch = git2::Patch::from_diff(diff, delta_idx).ok().flatten()?; let buf = patch.to_buf().ok()?; - let text = buf.as_str()?; + // git2 0.21: Buf::as_str() returns Result<&str, Utf8Error>. + let text = buf.as_str().ok()?; if text.trim().is_empty() { None } else { diff --git a/src/memory/store/vectors/store.rs b/src/memory/store/vectors/store.rs index fa5272c..3624dc5 100644 --- a/src/memory/store/vectors/store.rs +++ b/src/memory/store/vectors/store.rs @@ -22,6 +22,7 @@ use std::path::Path; use std::sync::Arc; +use anyhow::Context; use parking_lot::Mutex; use rusqlite::Connection; @@ -369,7 +370,10 @@ impl VectorStore { /// Returns the number of entries in a namespace (or all if `None`). pub fn count(&self, namespace: Option<&str>) -> anyhow::Result { let conn = self.conn.lock(); - let count: usize = match namespace { + // SQLite COUNT(*) is an i64; rusqlite (>= 0.33) no longer implements + // FromSql for usize, so read as i64 and convert. Overflow is impossible + // in practice but handled rather than silently truncated. + let count: i64 = match namespace { Some(ns) => conn.query_row( "SELECT COUNT(*) FROM vectors WHERE namespace = ?1", rusqlite::params![ns], @@ -377,7 +381,7 @@ impl VectorStore { )?, None => conn.query_row("SELECT COUNT(*) FROM vectors", [], |row| row.get(0))?, }; - Ok(count) + usize::try_from(count).context("vector count did not fit in usize") } /// Lists all distinct namespaces.