radicle: add Op::load method

Adds an `Op::load` helper method for loading an `Entry`, given its `Oid` and the
`store` it is stored in.

This can be useful for downstream consumers to inspect operations on COBs given
a single `Oid`, without having to load the entire object and/or graph.

It can also be useful when looking at implementing COB stream primitives.
This commit is contained in:
Fintan Halpenny 2025-01-16 10:42:18 +00:00
parent c847a16e14
commit 9ef9c5d59a
1 changed files with 35 additions and 0 deletions

View File

@ -3,6 +3,7 @@ use radicle_cob::Manifest;
use serde::Serialize; use serde::Serialize;
use thiserror::Error; use thiserror::Error;
use radicle_cob as cob;
use radicle_cob::history::{Entry, EntryId}; use radicle_cob::history::{Entry, EntryId};
use radicle_crypto::PublicKey; use radicle_crypto::PublicKey;
@ -23,6 +24,23 @@ pub enum OpEncodingError {
Git(#[from] git2::Error), Git(#[from] git2::Error),
} }
/// Error loading an `Op` from storage.
#[derive(Error, Debug)]
pub enum LoadError {
#[error("failed to load Op at '{object}': {err}")]
Load {
object: git::Oid,
#[source]
err: Box<dyn std::error::Error + Send + Sync + 'static>,
},
#[error("failed to decode Op at '{object}': {err}")]
Encoding {
object: git::Oid,
#[source]
err: OpEncodingError,
},
}
/// The `Op` is the operation that is applied onto a state to form a CRDT. /// The `Op` is the operation that is applied onto a state to form a CRDT.
/// ///
/// Everything that can be done in the system is represented by an `Op`. /// Everything that can be done in the system is represented by an `Op`.
@ -93,6 +111,23 @@ impl<A> Op<A> {
Some(head) => repo.identity_doc_at(head).map(Some), Some(head) => repo.identity_doc_at(head).map(Some),
} }
} }
/// Get the `Op` identified by the `id` in the provided `store`.
pub fn load<S>(store: &S, id: git::Oid) -> Result<Self, LoadError>
where
S: cob::change::Storage<
ObjectId = git::Oid,
Parent = git::Oid,
Signatures = crypto::ssh::ExtendedSignature,
>,
for<'de> A: serde::Deserialize<'de>,
{
let entry = store.load(id).map_err(|err| LoadError::Load {
object: id,
err: Box::new(err),
})?;
Op::try_from(&entry).map_err(|err| LoadError::Encoding { object: id, err })
}
} }
impl From<Entry> for Op<Vec<u8>> { impl From<Entry> for Op<Vec<u8>> {