use bytes::Bytes;
use cid::Cid as IpldCid;
use fjall::OwnedWriteBatch;
use jacquard_common::{CowStr, types::string::Handle};
use miette::{Context, IntoDiagnostic, Result};
use serde::{Deserialize, Serialize};
use crate::db::{
Db,
types::{DbTid, DidKey},
};
use crate::types::Commit;
use crate::types::v2::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(bound(deserialize = "'i: 'de"))]
pub(crate) struct OldRepoState<'i> {
pub status: RepoStatus, // from v2, old is same as new
pub rev: Option<DbTid>,
pub data: Option<IpldCid>,
#[serde(default)]
pub last_message_time: Option<i64>,
pub last_updated_at: i64,
pub tracked: bool,
pub index_id: u64,
#[serde(borrow)]
pub signing_key: Option<DidKey<'i>>,
#[serde(borrow)]
pub pds: Option<CowStr<'i>>,
pub handle: Option<Handle>,
}
pub(super) fn repo_state_root_commit(db: &Db, batch: &mut OwnedWriteBatch) -> Result<bool> {
for item in db.repos.iter() {
let (k, v) = item.into_inner().into_diagnostic()?;
let old: OldRepoState = rmp_serde::from_slice(&v)
.into_diagnostic()
.wrap_err("invalid old repo state")?;
let new = RepoState {
root: match (old.rev, old.data) {
(Some(rev), Some(data)) => Some(Commit {
version: -1,
rev,
data,
prev: None,
sig: Bytes::new(),
}),
_ => None,
},
status: old.status,
handle: old.handle,
index_id: old.index_id,
last_message_time: old.last_message_time,
last_updated_at: old.last_updated_at,
pds: old.pds,
signing_key: old.signing_key,
tracked: old.tracked,
};
batch.insert(
&db.repos,
k,
rmp_serde::to_vec(&new)
.into_diagnostic()
.wrap_err("cant serialize new repo state")?,
);
}
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
use jacquard_common::types::tid::Tid;
#[test]
fn v1_repo_state_input_golden_fixture() {
let state = OldRepoState {
status: RepoStatus::Error("stinkpot".into()),
rev: Some(DbTid::from(&Tid::new("3jzfcijpj2z2a").unwrap())),
data: Some(crate::car::compute_cid(b"root")),
last_message_time: Some(-1),
last_updated_at: 42,
tracked: true,
index_id: 7,
signing_key: Some(DidKey(std::borrow::Cow::Borrowed(&[0xed, 1, 2, 3]))),
pds: Some(CowStr::Borrowed("https://pds.example")),
handle: Some(Handle::new_static("alice.test").unwrap()),
};
let encoded = rmp_serde::to_vec(&state).unwrap();
assert_eq!(
crate::util::hex::encode(&encoded),
"9a81a54572726f72a87374696e6b706f74c40817fd6873eaf07c06c424017112204813494d137e1631bba301d5acab6e7bb7aa74ce1185d456565ef51d737677b2ff2ac307c404ed010203b368747470733a2f2f7064732e6578616d706c65aa616c6963652e74657374"
);
let decoded: OldRepoState<'_> = rmp_serde::from_slice(&encoded).unwrap();
assert!(matches!(&decoded.status, RepoStatus::Error(value) if value == "stinkpot"));
assert_eq!(decoded.rev, state.rev);
assert_eq!(decoded.data, state.data);
assert_eq!(decoded.last_message_time, Some(-1));
assert_eq!(decoded.last_updated_at, 42);
assert!(decoded.tracked);
assert_eq!(decoded.index_id, 7);
assert_eq!(decoded.signing_key, state.signing_key);
assert_eq!(decoded.pds.as_deref(), Some("https://pds.example"));
assert_eq!(
decoded.handle.as_ref().map(Handle::as_str),
Some("alice.test")
);
}
}