use std::error::Error;
use std::fmt;
use std::marker::PhantomData;
use crate::blob::encodings::simplearchive::SimpleArchive;
use crate::blob::{Blob, BlobEncoding};
use crate::metadata::{self, MetaDescribe};
use crate::prelude::{exists, pattern};
use crate::repo::{BlobStoreGet, BlobStoreMeta, StoreRead};
use crate::trible::Fragment;
use super::{
collection_representation, CollectionData, CollectionHandle, KIND_COLLECTION_DESCRIPTOR,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CollectionOperationError {
Fatal(String),
Capacity(String),
MissingDependency(CollectionData),
}
impl fmt::Display for CollectionOperationError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Fatal(reason) | Self::Capacity(reason) => formatter.write_str(reason),
Self::MissingDependency(member) => write!(
formatter,
"collection operation requires resident blob {}",
hex::encode_upper(member.raw),
),
}
}
}
impl Error for CollectionOperationError {}
pub trait CollectionEncoding: BlobEncoding + MetaDescribe + Sized + 'static {
fn validate_descriptor(_descriptor: &Fragment) -> Result<(), CollectionOperationError> {
Ok(())
}
fn validate_member<R>(
descriptor: &Fragment,
member: &Blob<Self>,
reader: &R,
) -> Result<(), CollectionOperationError>
where
R: BlobStoreGet + BlobStoreMeta;
fn missing_representation_dependencies<R>(
_member: CollectionData,
_reader: &R,
) -> Result<Vec<CollectionData>, CollectionOperationError>
where
R: BlobStoreGet + BlobStoreMeta,
{
Ok(Vec::new())
}
fn join_members<R>(
descriptor: &Fragment,
low: &Blob<Self>,
high: &Blob<Self>,
reader: &R,
) -> Result<Blob<Self>, CollectionOperationError>
where
R: BlobStoreGet + BlobStoreMeta;
fn join_many<R>(
descriptor: &Fragment,
members: &[Blob<Self>],
reader: &R,
) -> Result<Blob<Self>, CollectionOperationError>
where
R: BlobStoreGet + BlobStoreMeta,
{
let (first, rest) = members.split_first().ok_or_else(|| {
CollectionOperationError::Fatal("a join needs at least one member".to_owned())
})?;
let mut joined = first.clone();
for member in rest {
joined = Self::join_members(descriptor, &joined, member, reader)?;
}
Ok(joined)
}
}
pub trait CollectionDerivation: CollectionEncoding {
type Source: CollectionEncoding;
type Argument;
fn fragment(argument: &Self::Argument) -> Fragment;
fn bind(
source: &Fragment,
target: &Fragment,
) -> Result<Self::Argument, CollectionOperationError>;
fn map<R>(
argument: &Self::Argument,
source: &Blob<Self::Source>,
reader: &R,
) -> Result<Blob<Self>, CollectionOperationError>
where
R: StoreRead;
fn join_images<R>(
_argument: &Self::Argument,
target_descriptor: &Fragment,
images: &[Blob<Self>],
reader: &R,
) -> Result<Blob<Self>, CollectionOperationError>
where
R: BlobStoreGet + BlobStoreMeta,
{
Self::join_many(target_descriptor, images, reader)
}
}
pub trait DeriveMapping: Sized {
type Source: CollectionEncoding;
type Target: CollectionEncoding;
fn fragment(&self) -> Fragment;
fn bind(source: &Fragment, target: &Fragment) -> Result<Self, CollectionOperationError>;
fn map<R>(
&self,
source: &Blob<Self::Source>,
reader: &R,
) -> Result<Blob<Self::Target>, CollectionOperationError>
where
R: StoreRead;
fn computable_here(&self) -> bool {
true
}
fn join_images<R>(
&self,
target_descriptor: &Fragment,
images: &[Blob<Self::Target>],
reader: &R,
) -> Result<Blob<Self::Target>, CollectionOperationError>
where
R: BlobStoreGet + BlobStoreMeta,
{
Self::Target::join_many(target_descriptor, images, reader)
}
}
pub(crate) struct CanonicalDerivation<T: CollectionDerivation> {
argument: T::Argument,
}
impl<T: CollectionDerivation> CanonicalDerivation<T> {
pub(crate) fn new(argument: T::Argument) -> Self {
Self { argument }
}
}
impl<T: CollectionDerivation> DeriveMapping for CanonicalDerivation<T> {
type Source = T::Source;
type Target = T;
fn fragment(&self) -> Fragment {
T::fragment(&self.argument)
}
fn bind(source: &Fragment, target: &Fragment) -> Result<Self, CollectionOperationError> {
T::bind(source, target).map(Self::new)
}
fn map<R>(
&self,
source: &Blob<Self::Source>,
reader: &R,
) -> Result<Blob<Self::Target>, CollectionOperationError>
where
R: StoreRead,
{
T::map(&self.argument, source, reader)
}
fn join_images<R>(
&self,
target_descriptor: &Fragment,
images: &[Blob<Self::Target>],
reader: &R,
) -> Result<Blob<Self::Target>, CollectionOperationError>
where
R: BlobStoreGet + BlobStoreMeta,
{
T::join_images(&self.argument, target_descriptor, images, reader)
}
}
pub trait MapMapping: Sized {
type Target: CollectionEncoding;
fn fragment(&self) -> Fragment;
fn bind(parent: &Fragment, attached: &Fragment) -> Result<Self, CollectionOperationError>;
fn siblings(&self) -> Vec<CollectionHandle> {
Vec::new()
}
fn map<R>(
&self,
node: &Blob<SimpleArchive>,
siblings: &[CollectionData],
reader: &R,
) -> Result<Blob<Self::Target>, CollectionOperationError>
where
R: StoreRead;
}
pub trait CollectionAttachment: CollectionEncoding {
type Argument;
fn fragment(argument: &Self::Argument) -> Fragment;
fn bind(
parent: &Fragment,
attached: &Fragment,
) -> Result<Self::Argument, CollectionOperationError>;
fn siblings(_argument: &Self::Argument) -> Vec<CollectionHandle> {
Vec::new()
}
fn map<R>(
argument: &Self::Argument,
node: &Blob<SimpleArchive>,
siblings: &[CollectionData],
reader: &R,
) -> Result<Blob<Self>, CollectionOperationError>
where
R: StoreRead;
}
pub(crate) struct CanonicalAttachment<T: CollectionAttachment> {
argument: T::Argument,
}
impl<T: CollectionAttachment> CanonicalAttachment<T> {
pub(crate) fn new(argument: T::Argument) -> Self {
Self { argument }
}
}
impl<T: CollectionAttachment> MapMapping for CanonicalAttachment<T> {
type Target = T;
fn fragment(&self) -> Fragment {
T::fragment(&self.argument)
}
fn bind(parent: &Fragment, attached: &Fragment) -> Result<Self, CollectionOperationError> {
T::bind(parent, attached).map(Self::new)
}
fn siblings(&self) -> Vec<CollectionHandle> {
T::siblings(&self.argument)
}
fn map<R>(
&self,
node: &Blob<SimpleArchive>,
siblings: &[CollectionData],
reader: &R,
) -> Result<Blob<Self::Target>, CollectionOperationError>
where
R: StoreRead,
{
T::map(&self.argument, node, siblings, reader)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CollectionTypeError {
WrongEncoding {
expected: crate::id::Id,
},
InvalidDescriptor(CollectionOperationError),
}
impl fmt::Display for CollectionTypeError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::WrongEncoding { expected } => write!(
formatter,
"no collection descriptor names encoding {expected:X}",
),
Self::InvalidDescriptor(source) => {
write!(formatter, "invalid collection encoding context: {source}")
}
}
}
}
impl Error for CollectionTypeError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::InvalidDescriptor(source) => Some(source),
Self::WrongEncoding { .. } => None,
}
}
}
pub(crate) fn validate_descriptor_type<E>(
descriptor_fragment: &Fragment,
) -> Result<(), CollectionTypeError>
where
E: CollectionEncoding,
{
let expected = E::id();
if !exists!(pattern!(descriptor_fragment.facts(), [{ _?descriptor @
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
collection_representation: expected,
}])) {
return Err(CollectionTypeError::WrongEncoding { expected });
}
E::validate_descriptor(descriptor_fragment).map_err(CollectionTypeError::InvalidDescriptor)
}
pub struct Collection<E: CollectionEncoding> {
handle: CollectionHandle,
encoding: PhantomData<fn() -> E>,
}
impl<E: CollectionEncoding> Collection<E> {
pub(crate) const fn from_handle(handle: CollectionHandle) -> Self {
Self {
handle,
encoding: PhantomData,
}
}
pub const fn handle(self) -> CollectionHandle {
self.handle
}
}
impl<E: CollectionEncoding> Clone for Collection<E> {
fn clone(&self) -> Self {
*self
}
}
impl<E: CollectionEncoding> Copy for Collection<E> {}
impl<E: CollectionEncoding> fmt::Debug for Collection<E> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_tuple("Collection")
.field(&hex::encode_upper(self.handle.raw))
.finish()
}
}
impl<E: CollectionEncoding> PartialEq for Collection<E> {
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle
}
}
impl<E: CollectionEncoding> Eq for Collection<E> {}
impl<E: CollectionEncoding> PartialOrd for Collection<E> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<E: CollectionEncoding> Ord for Collection<E> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.handle.cmp(&other.handle)
}
}
impl<E: CollectionEncoding> std::hash::Hash for Collection<E> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.handle.hash(state);
}
}
impl<E: CollectionEncoding> From<Collection<E>> for CollectionHandle {
fn from(collection: Collection<E>) -> Self {
collection.handle
}
}