use std::hash::Hash;
use armour_core::{
CollectionShape, Fuid, GetRefs, Id64, IdBrand, StorageBackend, StorageClass, ValueModel,
};
use crate::armour::collection::Collection;
use crate::key::Key;
use crate::{
Codec, CollectionMeta, DbResult, TypedMap, TypedTree, TypedWriteHook, ZeroMap, ZeroTree,
};
pub trait IndexTarget: SchemaCollection {
fn put_entry(&self, k: &Self::K, v: &Self::V) -> DbResult<()>;
fn delete_entry(&self, k: &Self::K) -> DbResult<()>;
}
pub trait BrandedKey: Key {
const BRAND: &'static str;
fn from_id(id: u64) -> Self;
fn id_value(&self) -> u64;
}
impl<H: IdBrand> BrandedKey for Fuid<H>
where
Fuid<H>: Key,
{
const BRAND: &'static str = H::BRAND;
fn from_id(id: u64) -> Self {
Fuid::from_be_bytes(id.to_be_bytes())
}
fn id_value(&self) -> u64 {
self.get()
}
}
impl<T: IdBrand> BrandedKey for Id64<T>
where
Id64<T>: Key,
{
const BRAND: &'static str = T::BRAND;
fn from_id(id: u64) -> Self {
Id64::from_be_bytes(id.to_be_bytes())
}
fn id_value(&self) -> u64 {
self.get()
}
}
pub trait SchemaCollection: Collection + Send + Sync + 'static {
type K: Key + GetRefs + 'static;
type V: CollectionMeta + GetRefs + 'static;
const STORAGE: StorageClass;
fn scan(&self, f: &mut dyn FnMut(&Self::K, &Self::V));
fn contains_key(&self, key: &Self::K) -> bool;
fn with_value(&self, key: &Self::K, f: &mut dyn FnMut(&Self::V)) -> bool;
}
impl<T, C, H> SchemaCollection for TypedTree<T::SelfId, T, C, H>
where
T: CollectionMeta + GetRefs + Clone + Send + Sync + 'static,
T::SelfId: Key + GetRefs + Ord + Send + Sync + 'static,
C: Codec<T> + Default + Send + Sync + 'static,
H: TypedWriteHook<T::SelfId, T> + 'static,
{
type K = T::SelfId;
type V = T;
const STORAGE: StorageClass = StorageClass {
model: ValueModel::Typed,
shape: CollectionShape::Tree,
backend: StorageBackend::Bitcask,
};
fn scan(&self, f: &mut dyn FnMut(&Self::K, &Self::V)) {
for (k, v) in self.iter() {
f(&k, v);
}
}
fn contains_key(&self, key: &Self::K) -> bool {
self.get(key).is_some()
}
fn with_value(&self, key: &Self::K, f: &mut dyn FnMut(&Self::V)) -> bool {
match self.get(key) {
Some(v) => {
f(&*v);
true
}
None => false,
}
}
}
impl<T, C, H> IndexTarget for TypedTree<T::SelfId, T, C, H>
where
T: CollectionMeta + GetRefs + Clone + Send + Sync + 'static,
T::SelfId: Key + GetRefs + Ord + Send + Sync + 'static,
C: Codec<T> + Default + Send + Sync + 'static,
H: TypedWriteHook<T::SelfId, T> + 'static,
{
fn put_entry(&self, k: &Self::K, v: &Self::V) -> DbResult<()> {
self.put(k, v.clone()).map(drop)
}
fn delete_entry(&self, k: &Self::K) -> DbResult<()> {
self.delete(k).map(drop)
}
}
impl<T, C, H> SchemaCollection for TypedMap<T::SelfId, T, C, H>
where
T: CollectionMeta + GetRefs + Clone + Send + Sync + 'static,
T::SelfId: Key + GetRefs + Hash + Eq + Send + Sync + 'static,
C: Codec<T> + Default + Send + Sync + 'static,
H: TypedWriteHook<T::SelfId, T> + 'static,
{
type K = T::SelfId;
type V = T;
const STORAGE: StorageClass = StorageClass {
model: ValueModel::Typed,
shape: CollectionShape::Map,
backend: StorageBackend::Bitcask,
};
fn scan(&self, f: &mut dyn FnMut(&Self::K, &Self::V)) {
self.for_each(|k, v| f(k, v));
}
fn contains_key(&self, key: &Self::K) -> bool {
self.contains(key)
}
fn with_value(&self, key: &Self::K, f: &mut dyn FnMut(&Self::V)) -> bool {
match self.get(key) {
Some(v) => {
f(&*v);
true
}
None => false,
}
}
}
impl<T, C, H> IndexTarget for TypedMap<T::SelfId, T, C, H>
where
T: CollectionMeta + GetRefs + Clone + Send + Sync + 'static,
T::SelfId: Key + GetRefs + Hash + Eq + Send + Sync + 'static,
C: Codec<T> + Default + Send + Sync + 'static,
H: TypedWriteHook<T::SelfId, T> + 'static,
{
fn put_entry(&self, k: &Self::K, v: &Self::V) -> DbResult<()> {
self.put(k, v.clone()).map(drop)
}
fn delete_entry(&self, k: &Self::K) -> DbResult<()> {
self.delete(k).map(drop)
}
}
macro_rules! zero_tree_schema_impls {
($dur:ty, $backend:expr $(,)?) => {
impl<T, const V: usize, H> SchemaCollection for ZeroTree<T::SelfId, V, T, H, $dur>
where
T: CollectionMeta
+ GetRefs
+ Copy
+ zerocopy::IntoBytes
+ zerocopy::FromBytes
+ zerocopy::Immutable
+ Send
+ Sync
+ 'static,
T::SelfId: Key + GetRefs + Ord + Send + Sync + 'static,
H: TypedWriteHook<T::SelfId, T> + 'static,
{
type K = T::SelfId;
type V = T;
const STORAGE: StorageClass = StorageClass {
model: ValueModel::Zero,
shape: CollectionShape::Tree,
backend: $backend,
};
fn scan(&self, f: &mut dyn FnMut(&Self::K, &Self::V)) {
for (k, v) in self.iter() {
f(&k, &v);
}
}
fn contains_key(&self, key: &Self::K) -> bool {
self.contains(key)
}
fn with_value(&self, key: &Self::K, f: &mut dyn FnMut(&Self::V)) -> bool {
match self.get(key) {
Some(v) => {
f(&v);
true
}
None => false,
}
}
}
impl<T, const V: usize, H> IndexTarget for ZeroTree<T::SelfId, V, T, H, $dur>
where
T: CollectionMeta
+ GetRefs
+ Copy
+ zerocopy::IntoBytes
+ zerocopy::FromBytes
+ zerocopy::Immutable
+ Send
+ Sync
+ 'static,
T::SelfId: Key + GetRefs + Ord + Send + Sync + 'static,
H: TypedWriteHook<T::SelfId, T> + 'static,
{
fn put_entry(&self, k: &Self::K, v: &Self::V) -> DbResult<()> {
self.put(k, v).map(drop)
}
fn delete_entry(&self, k: &Self::K) -> DbResult<()> {
self.delete(k).map(drop)
}
}
};
}
zero_tree_schema_impls!(crate::durability::Bitcask, StorageBackend::Bitcask);
zero_tree_schema_impls!(crate::durability::Fixed, StorageBackend::Fixed);
macro_rules! zero_map_schema_impls {
($dur:ty, $backend:expr $(,)?) => {
impl<T, const V: usize, H> SchemaCollection for ZeroMap<T::SelfId, V, T, H, $dur>
where
T: CollectionMeta
+ GetRefs
+ Copy
+ zerocopy::IntoBytes
+ zerocopy::FromBytes
+ zerocopy::Immutable
+ Send
+ Sync
+ 'static,
T::SelfId: Key + GetRefs + Hash + Eq + Send + Sync + 'static,
H: TypedWriteHook<T::SelfId, T> + 'static,
{
type K = T::SelfId;
type V = T;
const STORAGE: StorageClass = StorageClass {
model: ValueModel::Zero,
shape: CollectionShape::Map,
backend: $backend,
};
fn scan(&self, f: &mut dyn FnMut(&Self::K, &Self::V)) {
self.for_each(|k, v| f(k, v));
}
fn contains_key(&self, key: &Self::K) -> bool {
self.contains(key)
}
fn with_value(&self, key: &Self::K, f: &mut dyn FnMut(&Self::V)) -> bool {
match self.get(key) {
Some(v) => {
f(&v);
true
}
None => false,
}
}
}
impl<T, const V: usize, H> IndexTarget for ZeroMap<T::SelfId, V, T, H, $dur>
where
T: CollectionMeta
+ GetRefs
+ Copy
+ zerocopy::IntoBytes
+ zerocopy::FromBytes
+ zerocopy::Immutable
+ Send
+ Sync
+ 'static,
T::SelfId: Key + GetRefs + Hash + Eq + Send + Sync + 'static,
H: TypedWriteHook<T::SelfId, T> + 'static,
{
fn put_entry(&self, k: &Self::K, v: &Self::V) -> DbResult<()> {
self.put(k, v).map(drop)
}
fn delete_entry(&self, k: &Self::K) -> DbResult<()> {
self.delete(k).map(drop)
}
}
};
}
zero_map_schema_impls!(crate::durability::Bitcask, StorageBackend::Bitcask);
zero_map_schema_impls!(crate::durability::Fixed, StorageBackend::Fixed);
#[cfg(feature = "var-collections")]
use crate::{VarTypedMap, VarTypedTree};
#[cfg(feature = "var-collections")]
impl<T, C, H> SchemaCollection for VarTypedTree<T::SelfId, T, C, H>
where
T: CollectionMeta + GetRefs + Send + Sync + 'static,
T::SelfId: Key + GetRefs + Ord + Send + Sync + 'static,
C: Codec<T> + Clone + Send + Sync + 'static,
H: TypedWriteHook<T::SelfId, T> + 'static,
{
type K = T::SelfId;
type V = T;
const STORAGE: StorageClass = StorageClass {
model: ValueModel::VarTyped,
shape: CollectionShape::Tree,
backend: StorageBackend::Bitcask,
};
fn scan(&self, f: &mut dyn FnMut(&Self::K, &Self::V)) {
let codec = self.codec();
for (k, bytes) in self.as_inner().iter() {
match codec.decode_from(&bytes) {
Ok(v) => f(&k, &v),
Err(_) => tracing::warn!(
collection = T::NAME,
"schema scan: decode error in VarTypedTree, skipping entry — relations not checked"
),
}
}
}
fn contains_key(&self, key: &Self::K) -> bool {
self.contains(key)
}
fn with_value(&self, key: &Self::K, f: &mut dyn FnMut(&Self::V)) -> bool {
match self.get(key) {
Some(v) => {
f(&v);
true
}
None => false,
}
}
}
#[cfg(feature = "var-collections")]
impl<T, C, H> IndexTarget for VarTypedTree<T::SelfId, T, C, H>
where
T: CollectionMeta + GetRefs + Send + Sync + 'static,
T::SelfId: Key + GetRefs + Ord + Send + Sync + 'static,
C: Codec<T> + Clone + Send + Sync + 'static,
H: TypedWriteHook<T::SelfId, T> + 'static,
{
fn put_entry(&self, k: &Self::K, v: &Self::V) -> DbResult<()> {
self.put(k, v).map(drop)
}
fn delete_entry(&self, k: &Self::K) -> DbResult<()> {
self.delete(k).map(drop)
}
}
#[cfg(feature = "var-collections")]
impl<T, C, H> SchemaCollection for VarTypedMap<T::SelfId, T, C, H>
where
T: CollectionMeta + GetRefs + Send + Sync + 'static,
T::SelfId: Key + GetRefs + Hash + Eq + Send + Sync + 'static,
C: Codec<T> + Clone + Send + Sync + 'static,
H: TypedWriteHook<T::SelfId, T> + 'static,
{
type K = T::SelfId;
type V = T;
const STORAGE: StorageClass = StorageClass {
model: ValueModel::VarTyped,
shape: CollectionShape::Map,
backend: StorageBackend::Bitcask,
};
fn scan(&self, f: &mut dyn FnMut(&Self::K, &Self::V)) {
let codec = self.codec();
self.as_inner()
.for_each(|k, bytes| match codec.decode_from(&bytes) {
Ok(v) => f(&k, &v),
Err(_) => tracing::warn!(
collection = T::NAME,
"schema scan: decode error in VarTypedMap, skipping entry — relations not checked"
),
});
}
fn contains_key(&self, key: &Self::K) -> bool {
self.contains(key)
}
fn with_value(&self, key: &Self::K, f: &mut dyn FnMut(&Self::V)) -> bool {
match self.get(key) {
Some(v) => {
f(&v);
true
}
None => false,
}
}
}
#[cfg(feature = "var-collections")]
impl<T, C, H> IndexTarget for VarTypedMap<T::SelfId, T, C, H>
where
T: CollectionMeta + GetRefs + Send + Sync + 'static,
T::SelfId: Key + GetRefs + Hash + Eq + Send + Sync + 'static,
C: Codec<T> + Clone + Send + Sync + 'static,
H: TypedWriteHook<T::SelfId, T> + 'static,
{
fn put_entry(&self, k: &Self::K, v: &Self::V) -> DbResult<()> {
self.put(k, v).map(drop)
}
fn delete_entry(&self, k: &Self::K) -> DbResult<()> {
self.delete(k).map(drop)
}
}