use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::future::Future;
use ed25519_dalek::SigningKey;
use crate::blob::encodings::entity_id_set::EntityIdSetBlob;
use crate::blob::encodings::succinctarchive::{
Rank9AcceleratedSuccinctArchiveBlob, SuccinctArchiveBlob,
};
use crate::id::Id;
use crate::inline::encodings::hash::Handle;
use crate::inline::InlineEncoding;
use crate::metadata::MetaDescribe;
use crate::repo::async_store::AsyncBlobStoreAcquire;
use crate::repo::{Store, StoreRead};
use crate::trible::TribleSet;
use super::api::{load_collection_descriptor, CollectionStoreExt};
use super::encoding::{CollectionAttachment, CollectionDerivation};
use super::exact_derived::CollectionRealizationError;
use super::latest::LatestBlob;
use super::lww_register::LwwRegisterBlob;
use super::Collection;
use super::{descriptor, CollectionHandle};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Derived {
pub handle: CollectionHandle,
pub source: CollectionHandle,
pub representation: Id,
pub algorithm: Option<Id>,
}
pub fn derived_from<S>(
snapshot: &S,
source: CollectionHandle,
) -> Result<Vec<Derived>, S::RecordsError>
where
S: StoreRead,
{
let mut by_source: BTreeMap<CollectionHandle, Vec<Derived>> = BTreeMap::new();
for collection in snapshot.collections()? {
if collection == source {
continue;
}
let Ok(facts) = snapshot.get::<TribleSet, _>(collection) else {
continue;
};
let (Ok(Some(parent)), Ok(representation), Ok(algorithm)) = (
descriptor::source(&facts),
descriptor::representation(&facts),
descriptor::mapping_algorithm(&facts),
) else {
continue;
};
by_source.entry(parent).or_default().push(Derived {
handle: collection,
source: parent,
representation,
algorithm,
});
}
let mut ordered = Vec::new();
let mut seen = BTreeSet::from([source]);
let mut queue = VecDeque::from([source]);
while let Some(parent) = queue.pop_front() {
let Some(children) = by_source.get(&parent) else {
continue;
};
for child in children {
if seen.insert(child.handle) {
ordered.push(*child);
queue.push_back(child.handle);
}
}
}
Ok(ordered)
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Attached {
pub handle: CollectionHandle,
pub parent: CollectionHandle,
pub representation: Id,
pub algorithm: Option<Id>,
pub siblings: Vec<CollectionHandle>,
}
pub fn attached_to<S>(
snapshot: &S,
parent: CollectionHandle,
) -> Result<Vec<Attached>, S::RecordsError>
where
S: StoreRead,
{
let mut pending: BTreeMap<CollectionHandle, Attached> = BTreeMap::new();
for collection in snapshot.collections()? {
let Ok(facts) = snapshot.get::<TribleSet, _>(collection) else {
continue;
};
let (Ok(named), Ok(representation), Ok(algorithm), Ok(siblings)) = (
descriptor::parents(&facts),
descriptor::representation(&facts),
descriptor::mapping_algorithm(&facts),
descriptor::reads_attached(&facts),
) else {
continue;
};
if named.as_slice() != [parent] {
continue;
}
pending.insert(
collection,
Attached {
handle: collection,
parent,
representation,
algorithm,
siblings,
},
);
}
let mut ordered = Vec::new();
while !pending.is_empty() {
let ready: Vec<CollectionHandle> = pending
.values()
.filter(|attached| {
attached
.siblings
.iter()
.all(|sibling| !pending.contains_key(sibling))
})
.map(|attached| attached.handle)
.collect();
let next = if ready.is_empty() {
vec![*pending.keys().next().expect("pending is not empty")]
} else {
ready
};
for handle in next {
ordered.push(
pending
.remove(&handle)
.expect("a pending attached collection"),
);
}
}
Ok(ordered)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Upkeep {
Ensure,
Maintain,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Realized {
Done,
Unadmitted,
Unknown,
}
pub trait RealizeDerived<S>
where
S: Store + AsyncBlobStoreAcquire + Send,
{
fn realize(
&mut self,
store: &mut S,
derived: &Derived,
signer: &SigningKey,
upkeep: Upkeep,
) -> impl Future<Output = Result<Realized, CollectionRealizationError>> + Send;
fn realize_attached(
&mut self,
store: &mut S,
attached: &Attached,
signer: &SigningKey,
upkeep: Upkeep,
) -> impl Future<Output = Result<Realized, CollectionRealizationError>> + Send;
}
pub async fn realize_as<S, T>(
store: &mut S,
derived: &Derived,
signer: &SigningKey,
upkeep: Upkeep,
) -> Result<Realized, CollectionRealizationError>
where
S: Store + AsyncBlobStoreAcquire + Send,
T: CollectionDerivation + MetaDescribe,
Handle<T>: InlineEncoding,
{
let snapshot = store.snapshot().map_err(|error| {
CollectionRealizationError::storage("observe derived collection", error)
})?;
let collection: Collection<T> = Collection::open(&snapshot, derived.handle)
.map_err(|error| CollectionRealizationError::storage("open derived descriptor", error))?;
let target = load_collection_descriptor(&snapshot, derived.handle)
.map_err(|error| CollectionRealizationError::storage("read derived descriptor", error))?;
let source = load_collection_descriptor(&snapshot, derived.source)
.map_err(|error| CollectionRealizationError::storage("read source descriptor", error))?;
if T::bind(&source.fragment, &target.fragment).is_err() {
return Ok(Realized::Unknown);
}
let admitted = collection
.writer_is_admitted(&snapshot, signer.verifying_key())
.map_err(|error| CollectionRealizationError::storage("admit derived writer", error))?;
drop(snapshot);
match (upkeep, admitted) {
(Upkeep::Ensure, false) => return Ok(Realized::Unadmitted),
(Upkeep::Ensure, true) => drop(store.ensure(collection, signer).await?),
(Upkeep::Maintain, _) => drop(store.maintain(collection, signer).await?),
}
Ok(if admitted {
Realized::Done
} else {
Realized::Unadmitted
})
}
pub async fn realize_attached_as<S, T>(
store: &mut S,
attached: &Attached,
signer: &SigningKey,
upkeep: Upkeep,
) -> Result<Realized, CollectionRealizationError>
where
S: Store + AsyncBlobStoreAcquire + Send,
T: CollectionAttachment + MetaDescribe,
Handle<T>: InlineEncoding,
{
let snapshot = store.snapshot().map_err(|error| {
CollectionRealizationError::storage("observe attached collection", error)
})?;
let collection: Collection<T> = Collection::open(&snapshot, attached.handle)
.map_err(|error| CollectionRealizationError::storage("open attached descriptor", error))?;
let descriptor = load_collection_descriptor(&snapshot, attached.handle)
.map_err(|error| CollectionRealizationError::storage("read attached descriptor", error))?;
let parent = load_collection_descriptor(&snapshot, attached.parent)
.map_err(|error| CollectionRealizationError::storage("read parent descriptor", error))?;
drop(snapshot);
if T::bind(&parent.fragment, &descriptor.fragment).is_err() {
return Ok(Realized::Unknown);
}
match upkeep {
Upkeep::Ensure => drop(store.ensure_attached(collection, signer).await?),
Upkeep::Maintain => drop(store.maintain_attached(collection, signer).await?),
}
Ok(Realized::Done)
}
#[derive(Clone, Copy, Debug, Default)]
pub struct CoreRealizer;
impl<S> RealizeDerived<S> for CoreRealizer
where
S: Store + AsyncBlobStoreAcquire + Send,
{
async fn realize(
&mut self,
_store: &mut S,
_derived: &Derived,
_signer: &SigningKey,
_upkeep: Upkeep,
) -> Result<Realized, CollectionRealizationError> {
Ok(Realized::Unknown)
}
async fn realize_attached(
&mut self,
store: &mut S,
attached: &Attached,
signer: &SigningKey,
upkeep: Upkeep,
) -> Result<Realized, CollectionRealizationError> {
let representation = attached.representation;
if representation == <SuccinctArchiveBlob as MetaDescribe>::id() {
realize_attached_as::<S, SuccinctArchiveBlob>(store, attached, signer, upkeep).await
} else if representation == <Rank9AcceleratedSuccinctArchiveBlob as MetaDescribe>::id() {
realize_attached_as::<S, Rank9AcceleratedSuccinctArchiveBlob>(
store, attached, signer, upkeep,
)
.await
} else if representation == <EntityIdSetBlob as MetaDescribe>::id() {
realize_attached_as::<S, EntityIdSetBlob>(store, attached, signer, upkeep).await
} else if representation == <LatestBlob as MetaDescribe>::id() {
realize_attached_as::<S, LatestBlob>(store, attached, signer, upkeep).await
} else if representation == <LwwRegisterBlob as MetaDescribe>::id() {
realize_attached_as::<S, LwwRegisterBlob>(store, attached, signer, upkeep).await
} else {
Ok(Realized::Unknown)
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct UpkeepReport {
pub realized: Vec<CollectionHandle>,
pub unadmitted: Vec<CollectionHandle>,
pub unknown: Vec<Derived>,
pub unknown_attached: Vec<Attached>,
pub failed: Vec<(Derived, String)>,
pub failed_attached: Vec<(Attached, String)>,
}
pub async fn upkeep_downstream<S, R>(
store: &mut S,
source: CollectionHandle,
signer: &SigningKey,
upkeep: Upkeep,
realizer: &mut R,
) -> Result<UpkeepReport, CollectionRealizationError>
where
S: Store + AsyncBlobStoreAcquire + Send,
R: RealizeDerived<S>,
{
let snapshot = store.snapshot().map_err(|error| {
CollectionRealizationError::storage("observe derived collections", error)
})?;
let derived = derived_from(&snapshot, source)
.map_err(|error| CollectionRealizationError::storage("list derived collections", error))?;
let attached = attached_to(&snapshot, source)
.map_err(|error| CollectionRealizationError::storage("list attached collections", error))?;
drop(snapshot);
let mut report = UpkeepReport::default();
for target in derived {
match realizer.realize(store, &target, signer, upkeep).await {
Ok(Realized::Done) => report.realized.push(target.handle),
Ok(Realized::Unadmitted) => report.unadmitted.push(target.handle),
Ok(Realized::Unknown) => report.unknown.push(target),
Err(error @ CollectionRealizationError::HostMismatch { .. }) => return Err(error),
Err(error) => report.failed.push((target, error.to_string())),
}
}
for target in attached {
match realizer
.realize_attached(store, &target, signer, upkeep)
.await
{
Ok(Realized::Done) => report.realized.push(target.handle),
Ok(Realized::Unadmitted) => report.unadmitted.push(target.handle),
Ok(Realized::Unknown) => report.unknown_attached.push(target),
Err(error @ CollectionRealizationError::HostMismatch { .. }) => return Err(error),
Err(error) => report.failed_attached.push((target, error.to_string())),
}
}
Ok(report)
}
pub async fn ensure_downstream<S, R>(
store: &mut S,
source: CollectionHandle,
signer: &SigningKey,
realizer: &mut R,
) -> Result<UpkeepReport, CollectionRealizationError>
where
S: Store + AsyncBlobStoreAcquire + Send,
R: RealizeDerived<S>,
{
upkeep_downstream(store, source, signer, Upkeep::Ensure, realizer).await
}
pub async fn maintain_downstream<S, R>(
store: &mut S,
source: CollectionHandle,
signer: &SigningKey,
realizer: &mut R,
) -> Result<UpkeepReport, CollectionRealizationError>
where
S: Store + AsyncBlobStoreAcquire + Send,
R: RealizeDerived<S>,
{
upkeep_downstream(store, source, signer, Upkeep::Maintain, realizer).await
}