use crate::blob::encodings::UnknownBlob;
use crate::blob::Blob;
use crate::blob::BlobEncoding;
use crate::blob::IntoBlob;
use crate::patch::{Entry, IdentitySchema, PATCH};
use crate::repo::BlobStore;
use crate::repo::BlobStoreGet;
use crate::repo::BlobStoreKeep;
use crate::repo::BlobStoreList;
use crate::repo::BlobStorePut;
use crate::inline::encodings::hash::Handle;
use crate::inline::Inline;
use crate::inline::INLINE_LEN;
use std::convert::Infallible;
use std::error::Error;
use std::fmt::Debug;
use std::fmt::{self};
use std::iter::FromIterator;
use super::TryFromBlob;
pub struct MemoryBlobStore {
blobs: PATCH<INLINE_LEN, IdentitySchema, Blob<UnknownBlob>>,
}
impl Debug for MemoryBlobStore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MemoryBlobStore")
}
}
#[derive(Debug)]
pub struct MemoryBlobStoreReader {
blobs: PATCH<INLINE_LEN, IdentitySchema, Blob<UnknownBlob>>,
}
impl Clone for MemoryBlobStoreReader {
fn clone(&self) -> Self {
MemoryBlobStoreReader {
blobs: self.blobs.clone(),
}
}
}
impl PartialEq for MemoryBlobStoreReader {
fn eq(&self, other: &Self) -> bool {
self.blobs == other.blobs
}
}
impl Eq for MemoryBlobStoreReader {}
impl MemoryBlobStoreReader {
fn new(blobs: PATCH<INLINE_LEN, IdentitySchema, Blob<UnknownBlob>>) -> Self {
MemoryBlobStoreReader { blobs }
}
pub fn len(&self) -> usize {
self.blobs.len() as usize
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter(&self) -> MemoryBlobStoreIter {
let for_iter = self.blobs.clone();
let lookup = for_iter.clone();
MemoryBlobStoreIter {
keys: for_iter.into_iter(),
lookup,
}
}
}
impl Clone for MemoryBlobStore {
fn clone(&self) -> Self {
MemoryBlobStore {
blobs: self.blobs.clone(),
}
}
}
impl PartialEq for MemoryBlobStore {
fn eq(&self, other: &Self) -> bool {
self.blobs == other.blobs
}
}
impl Eq for MemoryBlobStore {}
impl Default for MemoryBlobStore {
fn default() -> Self {
Self::new()
}
}
impl MemoryBlobStore {
pub fn new() -> MemoryBlobStore {
MemoryBlobStore {
blobs: PATCH::new(),
}
}
pub fn insert<S>(&mut self, blob: Blob<S>) -> Inline<Handle<S>>
where
S: BlobEncoding,
Handle<S>: crate::inline::InlineEncoding,
{
let handle: Inline<Handle<S>> = blob.get_handle();
let unknown_handle: Inline<Handle<UnknownBlob>> = handle.transmute();
let blob: Blob<UnknownBlob> = blob.transmute::<UnknownBlob>();
let entry = Entry::with_value(&unknown_handle.raw, blob);
self.blobs.insert(&entry);
handle
}
pub fn len(&self) -> usize {
self.blobs.len() as usize
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn union(&mut self, other: Self) {
self.blobs.union(other.blobs);
}
pub fn keep<I>(&mut self, handles: I)
where
I: IntoIterator<Item = Inline<Handle<UnknownBlob>>>,
{
let mut surviving = PATCH::new();
for handle in handles {
if let Some(blob) = self.blobs.get(&handle.raw) {
let entry = Entry::with_value(&handle.raw, blob.clone());
surviving.insert(&entry);
}
}
self.blobs = surviving;
}
}
impl BlobStoreKeep for MemoryBlobStore {
fn keep<I>(&mut self, handles: I)
where
I: IntoIterator<Item = Inline<Handle<UnknownBlob>>>,
{
MemoryBlobStore::keep(self, handles);
}
}
impl FromIterator<(Inline<Handle<UnknownBlob>>, Blob<UnknownBlob>)> for MemoryBlobStore {
fn from_iter<I: IntoIterator<Item = (Inline<Handle<UnknownBlob>>, Blob<UnknownBlob>)>>(
iter: I,
) -> Self {
let mut store = MemoryBlobStore::new();
for (handle, blob) in iter {
let entry = Entry::with_value(&handle.raw, blob);
store.blobs.insert(&entry);
}
store
}
}
impl IntoIterator for MemoryBlobStoreReader {
type Item = (Inline<Handle<UnknownBlob>>, Blob<UnknownBlob>);
type IntoIter = MemoryBlobStoreIter;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug)]
pub enum MemoryStoreGetError<E: Error> {
NotFound(),
ConversionFailed(E),
}
impl<E: Error> fmt::Display for MemoryStoreGetError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemoryStoreGetError::NotFound() => write!(f, "Blob not found in memory store"),
MemoryStoreGetError::ConversionFailed(e) => write!(f, "Blob conversion failed: {e}"),
}
}
}
impl<E: Error> Error for MemoryStoreGetError<E> {}
pub struct MemoryBlobStoreIter {
keys: crate::patch::PATCHIntoIterator<INLINE_LEN, IdentitySchema, Blob<UnknownBlob>>,
lookup: PATCH<INLINE_LEN, IdentitySchema, Blob<UnknownBlob>>,
}
impl Debug for MemoryBlobStoreIter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MemoryBlobStoreIter").finish()
}
}
impl Iterator for MemoryBlobStoreIter {
type Item = (Inline<Handle<UnknownBlob>>, Blob<UnknownBlob>);
fn next(&mut self) -> Option<Self::Item> {
let key = self.keys.next()?;
let handle: Inline<Handle<UnknownBlob>> = Inline::new(key);
let blob = self
.lookup
.get(&key)
.cloned()
.expect("key from PATCH iterator must resolve in the same snapshot");
Some((handle, blob))
}
}
pub struct MemoryBlobStoreListIter {
inner: MemoryBlobStoreIter,
}
impl Iterator for MemoryBlobStoreListIter {
type Item = Result<Inline<Handle<UnknownBlob>>, Infallible>;
fn next(&mut self) -> Option<Self::Item> {
let (handle, _) = self.inner.next()?;
Some(Ok(handle))
}
}
impl BlobStoreList for MemoryBlobStoreReader {
type Iter<'a> = MemoryBlobStoreListIter;
type Err = Infallible;
fn blobs(&self) -> Self::Iter<'static> {
MemoryBlobStoreListIter { inner: self.iter() }
}
}
impl BlobStoreGet for MemoryBlobStoreReader {
type GetError<E: Error + Send + Sync + 'static> = MemoryStoreGetError<E>;
fn get<T, S>(
&self,
handle: Inline<Handle<S>>,
) -> Result<T, Self::GetError<<T as TryFromBlob<S>>::Error>>
where
S: BlobEncoding,
T: TryFromBlob<S>,
{
let handle: Inline<Handle<UnknownBlob>> = handle.transmute();
let Some(blob) = self.blobs.get(&handle.raw) else {
return Err(MemoryStoreGetError::NotFound());
};
let blob: Blob<S> = blob.clone().transmute();
match blob.try_from_blob() {
Ok(value) => Ok(value),
Err(e) => Err(MemoryStoreGetError::ConversionFailed(e)),
}
}
}
impl crate::repo::BlobChildren for MemoryBlobStoreReader {}
impl BlobStorePut for MemoryBlobStore {
type PutError = Infallible;
fn put<S, T>(&mut self, item: T) -> Result<Inline<Handle<S>>, Self::PutError>
where
S: BlobEncoding,
T: IntoBlob<S>,
{
let blob = item.to_blob();
let handle = blob.get_handle();
self.insert(blob);
Ok(handle)
}
}
impl BlobStore for MemoryBlobStore {
type Reader = MemoryBlobStoreReader;
type ReaderError = Infallible;
fn reader(&mut self) -> Result<Self::Reader, Self::ReaderError> {
Ok(MemoryBlobStoreReader::new(self.blobs.clone()))
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use super::*;
use anybytes::Bytes;
use fake::faker::name::raw::Name;
use fake::locales::EN;
use fake::Fake;
use blobencodings::LongString;
use inlineencodings::Handle;
attributes! {
"5AD0FAFB1FECBC197A385EC20166899E" as description: Handle<LongString>;
}
#[test]
fn keep() {
use crate::repo::potential_handles;
use crate::trible::TribleSet;
let mut kb = TribleSet::new();
let mut blobs = MemoryBlobStore::new();
for _i in 0..200 {
kb += entity! {
description: blobs.put(Bytes::from_source(Name(EN).fake::<String>()).view().unwrap()).unwrap()
};
}
blobs.keep(potential_handles(&kb));
}
#[test]
fn reader_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<MemoryBlobStoreReader>();
}
#[test]
fn reader_is_a_pinned_snapshot() {
let mut store = MemoryBlobStore::new();
let blob_a: Inline<Handle<LongString>> =
store.put(Bytes::from_source("hello".to_string()).view().unwrap()).unwrap();
let snapshot = store.reader().unwrap();
assert_eq!(snapshot.len(), 1);
let _blob_b: Inline<Handle<LongString>> =
store.put(Bytes::from_source("world".to_string()).view().unwrap()).unwrap();
assert_eq!(snapshot.len(), 1);
use anybytes::View;
let recovered: View<str> =
snapshot.get::<View<str>, LongString>(blob_a).unwrap();
assert_eq!(&*recovered, "hello");
let fresh = store.reader().unwrap();
assert_eq!(fresh.len(), 2);
}
#[test]
fn union_merges_and_preserves_handles() {
let mut a = MemoryBlobStore::new();
let h_hello: Inline<Handle<LongString>> = a
.put(Bytes::from_source("hello".to_string()).view().unwrap())
.unwrap();
let mut b = MemoryBlobStore::new();
let h_world: Inline<Handle<LongString>> = b
.put(Bytes::from_source("world".to_string()).view().unwrap())
.unwrap();
let _h_hello_b: Inline<Handle<LongString>> = b
.put(Bytes::from_source("hello".to_string()).view().unwrap())
.unwrap();
a.union(b);
assert_eq!(a.reader().unwrap().len(), 2, "duplicates collapse via union");
use anybytes::View;
let recovered_hello: View<str> = a
.reader()
.unwrap()
.get::<View<str>, LongString>(h_hello)
.unwrap();
assert_eq!(&*recovered_hello, "hello");
let recovered_world: View<str> = a
.reader()
.unwrap()
.get::<View<str>, LongString>(h_world)
.unwrap();
assert_eq!(&*recovered_world, "world");
}
}