use std::collections::BTreeSet;
use std::error::Error;
use std::fmt::Debug;
use super::coverage::{Coverage, CoverageIndex};
use super::{CollectionData, CollectionHandle, CollectionRecord};
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum CollectionRecordSelector {
Collection(CollectionHandle),
CommitMember(CollectionHandle, CollectionData),
ProducedMember(CollectionHandle, CollectionData),
MergeCollection(CollectionHandle),
DeriveTarget(CollectionHandle),
Foundations(CollectionHandle),
}
pub(crate) fn selectors_match_record(
selectors: &BTreeSet<CollectionRecordSelector>,
record: CollectionRecord,
) -> bool {
let collection = record.collection();
if selectors.contains(&CollectionRecordSelector::Collection(collection)) {
return true;
}
if matches!(
record,
CollectionRecord::Commit(_) | CollectionRecord::Derive(_)
) && selectors.contains(&CollectionRecordSelector::Foundations(collection))
{
return true;
}
let output = match record {
CollectionRecord::Commit(commit) => commit.data(),
CollectionRecord::Merge(merge) => merge.result(),
CollectionRecord::Derive(derive) => derive.output(),
CollectionRecord::Map(map) => map.attachment(),
};
if selectors.contains(&CollectionRecordSelector::ProducedMember(
collection, output,
)) {
return true;
}
let matches_fields = match record {
CollectionRecord::Commit(commit) => selectors.contains(
&CollectionRecordSelector::CommitMember(commit.collection(), commit.data()),
),
CollectionRecord::Merge(merge) => selectors.contains(
&CollectionRecordSelector::MergeCollection(merge.collection()),
),
CollectionRecord::Derive(derive) => {
selectors.contains(&CollectionRecordSelector::DeriveTarget(derive.collection()))
}
CollectionRecord::Map(_) => false,
};
matches_fields
}
pub trait CollectionRead {
type RecordsError: Error + Debug + Send + Sync + 'static;
type RecordIter<'a>: Iterator<Item = Result<CollectionRecord, Self::RecordsError>>
where
Self: 'a;
fn records<'a>(&'a self) -> Result<Self::RecordIter<'a>, Self::RecordsError>;
fn collections(&self) -> Result<Vec<CollectionHandle>, Self::RecordsError>;
fn select_records(
&self,
selectors: &BTreeSet<CollectionRecordSelector>,
) -> Result<Vec<CollectionRecord>, Self::RecordsError> {
if selectors.is_empty() {
return Ok(Vec::new());
}
let records = self.records()?;
let mut selected = Vec::new();
for record in records {
let record = record?;
if selectors_match_record(selectors, record) {
selected.push(record);
}
}
Ok(selected)
}
fn select_record_changes(
&self,
previous: &Self,
selectors: &BTreeSet<CollectionRecordSelector>,
) -> Result<(Vec<CollectionRecord>, Vec<CollectionRecord>), Self::RecordsError> {
selected_record_changes(self, previous, selectors)
}
}
pub(crate) fn selected_record_changes<R: CollectionRead + ?Sized>(
current: &R,
previous: &R,
selectors: &BTreeSet<CollectionRecordSelector>,
) -> Result<(Vec<CollectionRecord>, Vec<CollectionRecord>), R::RecordsError> {
if selectors.is_empty() {
return Ok((Vec::new(), Vec::new()));
}
let current: BTreeSet<_> = current.select_records(selectors)?.into_iter().collect();
let previous: BTreeSet<_> = previous.select_records(selectors)?.into_iter().collect();
Ok((
current.difference(&previous).copied().collect(),
previous.difference(¤t).copied().collect(),
))
}
pub trait CoverageRead: CollectionRead {
fn index(
&self,
lineage: &BTreeSet<CollectionHandle>,
) -> Result<CoverageIndex, Self::RecordsError>;
fn coverage(
&self,
lineage: &BTreeSet<CollectionHandle>,
) -> Result<Coverage, Self::RecordsError> {
Ok(self.index(lineage)?.published().clone())
}
}
impl<R: CoverageRead> CoverageRead for &R {
fn index(
&self,
lineage: &BTreeSet<CollectionHandle>,
) -> Result<CoverageIndex, Self::RecordsError> {
(**self).index(lineage)
}
fn coverage(
&self,
lineage: &BTreeSet<CollectionHandle>,
) -> Result<Coverage, Self::RecordsError> {
(**self).coverage(lineage)
}
}
impl<R> CollectionRead for &R
where
R: CollectionRead + ?Sized,
{
type RecordsError = R::RecordsError;
type RecordIter<'a>
= R::RecordIter<'a>
where
Self: 'a;
fn records<'a>(&'a self) -> Result<Self::RecordIter<'a>, Self::RecordsError> {
(**self).records()
}
fn collections(&self) -> Result<Vec<CollectionHandle>, Self::RecordsError> {
(**self).collections()
}
fn select_records(
&self,
selectors: &BTreeSet<CollectionRecordSelector>,
) -> Result<Vec<CollectionRecord>, Self::RecordsError> {
(**self).select_records(selectors)
}
fn select_record_changes(
&self,
previous: &Self,
selectors: &BTreeSet<CollectionRecordSelector>,
) -> Result<(Vec<CollectionRecord>, Vec<CollectionRecord>), Self::RecordsError> {
(**self).select_record_changes(*previous, selectors)
}
}
pub fn distinct_collections(
records: impl IntoIterator<Item = CollectionRecord>,
) -> Vec<CollectionHandle> {
records
.into_iter()
.map(|record| record.collection())
.collect::<BTreeSet<_>>()
.into_iter()
.collect()
}
pub trait CollectionStore {
type InsertError: Error + Debug + Send + Sync + 'static;
fn insert(&mut self, record: CollectionRecord) -> Result<(), Self::InsertError>;
}
impl<S> CollectionStore for &mut S
where
S: CollectionStore + ?Sized,
{
type InsertError = S::InsertError;
fn insert(&mut self, record: CollectionRecord) -> Result<(), Self::InsertError> {
(**self).insert(record)
}
}
#[cfg(test)]
mod tests {
use std::cell::Cell;
use std::convert::Infallible;
use ed25519_dalek::SigningKey;
use super::*;
use crate::collection::{
empty_metadata_handle, CollectionCommit, CollectionData, CollectionDerive, CollectionMerge,
};
use crate::inline::Inline;
fn collection(byte: u8) -> CollectionHandle {
Inline::new([byte; 32])
}
fn data(byte: u8) -> CollectionData {
Inline::new([byte; 32])
}
fn fixture() -> Vec<CollectionRecord> {
let source = collection(1);
let target = collection(2);
let other = collection(3);
let input = data(10);
let mut records = vec![
CollectionRecord::Commit(CollectionCommit::sign(
&SigningKey::from_bytes(&[7; 32]),
source,
data(4),
empty_metadata_handle(),
)),
CollectionRecord::Merge(
CollectionMerge::sign(
&ed25519_dalek::SigningKey::from_bytes(&[7; 32]),
source,
[data(4), data(5)],
data(6),
)
.unwrap(),
),
CollectionRecord::Merge(
CollectionMerge::sign(
&ed25519_dalek::SigningKey::from_bytes(&[7; 32]),
other,
[data(4), data(5)],
data(7),
)
.unwrap(),
),
CollectionRecord::Derive(CollectionDerive::sign(
&ed25519_dalek::SigningKey::from_bytes(&[7; 32]),
target,
crate::collection::SourceLocator::of(input.raw),
data(11),
)),
CollectionRecord::Derive(CollectionDerive::sign(
&ed25519_dalek::SigningKey::from_bytes(&[7; 32]),
target,
crate::collection::SourceLocator::of(input.raw),
data(12),
)),
CollectionRecord::Derive(CollectionDerive::sign(
&ed25519_dalek::SigningKey::from_bytes(&[7; 32]),
target,
crate::collection::SourceLocator::of(data(13).raw),
data(14),
)),
CollectionRecord::Derive(CollectionDerive::sign(
&ed25519_dalek::SigningKey::from_bytes(&[7; 32]),
other,
crate::collection::SourceLocator::of(input.raw),
data(15),
)),
];
records.sort_unstable_by_key(CollectionRecord::fingerprint);
records
}
#[derive(Default)]
struct FallbackStore {
records: Vec<CollectionRecord>,
enumerations: Cell<usize>,
}
impl CollectionRead for FallbackStore {
type RecordsError = Infallible;
type RecordIter<'a> = std::vec::IntoIter<Result<CollectionRecord, Infallible>>;
fn records<'a>(&'a self) -> Result<Self::RecordIter<'a>, Self::RecordsError> {
self.enumerations.set(self.enumerations.get() + 1);
Ok(self
.records
.iter()
.copied()
.map(Ok)
.collect::<Vec<_>>()
.into_iter())
}
fn collections(&self) -> Result<Vec<CollectionHandle>, Self::RecordsError> {
Ok(distinct_collections(self.records.iter().copied()))
}
}
impl CollectionStore for FallbackStore {
type InsertError = Infallible;
fn insert(&mut self, record: CollectionRecord) -> Result<(), Self::InsertError> {
self.records.push(record);
self.records
.sort_unstable_by_key(CollectionRecord::fingerprint);
self.records.dedup_by_key(|record| record.fingerprint());
Ok(())
}
}
#[test]
fn field_selectors_do_not_recompute_record_fingerprints() {
use crate::collection::records::FINGERPRINT_CALLS;
let records = fixture();
let selectors = [
CollectionRecordSelector::Collection(collection(1)),
CollectionRecordSelector::Collection(collection(99)),
CollectionRecordSelector::CommitMember(collection(1), data(4)),
CollectionRecordSelector::ProducedMember(collection(2), data(11)),
CollectionRecordSelector::MergeCollection(collection(1)),
CollectionRecordSelector::DeriveTarget(collection(2)),
];
let before = FINGERPRINT_CALLS.get();
for selector in selectors {
for &record in &records {
selectors_match_record(&BTreeSet::from([selector]), record);
}
}
for &record in &records {
assert!(!selectors_match_record(&BTreeSet::new(), record));
}
assert_eq!(FINGERPRINT_CALLS.get() - before, 0);
}
#[test]
fn default_relationship_selection_does_not_require_present_witnesses() {
let records = fixture();
let store = FallbackStore {
records: records.clone(),
..FallbackStore::default()
};
let selectors = BTreeSet::from([
CollectionRecordSelector::ProducedMember(collection(1), data(4)),
CollectionRecordSelector::ProducedMember(collection(1), data(6)),
CollectionRecordSelector::ProducedMember(collection(2), data(11)),
]);
let selected = store.select_records(&selectors).unwrap();
assert_eq!(store.enumerations.get(), 1);
let expected: Vec<_> = records
.into_iter()
.filter(|record| match record {
CollectionRecord::Commit(commit) => {
commit.collection() == collection(1) && commit.data() == data(4)
}
CollectionRecord::Merge(merge) => {
merge.collection() == collection(1) && merge.result() == data(6)
}
CollectionRecord::Derive(derive) => {
derive.collection() == collection(2) && derive.output() == data(11)
}
CollectionRecord::Map(_) => false,
})
.collect();
assert_eq!(selected, expected);
assert!(selected
.windows(2)
.all(|pair| pair[0].fingerprint() < pair[1].fingerprint()));
}
#[test]
fn default_pair_selection_includes_all_inputs_and_excludes_other_pairs() {
let records = fixture();
let target = collection(2);
let pair = [CollectionRecordSelector::DeriveTarget(target)]
.into_iter()
.collect();
let store = FallbackStore {
records,
..FallbackStore::default()
};
let selected = store.select_records(&pair).unwrap();
assert_eq!(selected.len(), 3);
assert!(selected.iter().all(|record| match record {
CollectionRecord::Derive(derive) => derive.collection() == target,
_ => false,
}));
}
#[test]
fn exact_collection_selection_includes_every_record_kind_and_no_other_collection() {
let expected = collection(1);
let other = collection(2);
let author = SigningKey::from_bytes(&[9; 32]);
let mut records = vec![
CollectionRecord::Commit(CollectionCommit::sign(
&author,
expected,
data(1),
empty_metadata_handle(),
)),
CollectionRecord::Merge(
CollectionMerge::sign(
&ed25519_dalek::SigningKey::from_bytes(&[7; 32]),
expected,
[data(1), data(2)],
data(3),
)
.unwrap(),
),
CollectionRecord::Derive(CollectionDerive::sign(
&ed25519_dalek::SigningKey::from_bytes(&[7; 32]),
expected,
crate::collection::SourceLocator::of(data(3).raw),
data(4),
)),
CollectionRecord::Commit(CollectionCommit::sign(
&author,
other,
data(1),
empty_metadata_handle(),
)),
CollectionRecord::Merge(
CollectionMerge::sign(
&ed25519_dalek::SigningKey::from_bytes(&[7; 32]),
other,
[data(1), data(2)],
data(3),
)
.unwrap(),
),
CollectionRecord::Derive(CollectionDerive::sign(
&ed25519_dalek::SigningKey::from_bytes(&[7; 32]),
other,
crate::collection::SourceLocator::of(data(3).raw),
data(4),
)),
];
records.sort_unstable_by_key(CollectionRecord::fingerprint);
let store = FallbackStore {
records,
..FallbackStore::default()
};
let selectors = BTreeSet::from([CollectionRecordSelector::Collection(expected)]);
let selected = store.select_records(&selectors).unwrap();
assert_eq!(selected.len(), 3);
assert!(selected
.iter()
.any(|record| matches!(record, CollectionRecord::Commit(_))));
assert!(selected
.iter()
.any(|record| matches!(record, CollectionRecord::Merge(_))));
assert!(selected
.iter()
.any(|record| matches!(record, CollectionRecord::Derive(_))));
assert!(selected
.iter()
.all(|record| record.collection() == expected));
assert_eq!(store.enumerations.get(), 1);
}
#[test]
fn empty_selection_returns_without_enumeration() {
let store = FallbackStore {
records: fixture(),
..FallbackStore::default()
};
assert!(store.select_records(&BTreeSet::new()).unwrap().is_empty());
assert_eq!(store.enumerations.get(), 0);
}
#[derive(Default)]
struct OverrideStore {
records_calls: Cell<usize>,
selection_calls: Cell<usize>,
}
impl CollectionRead for OverrideStore {
type RecordsError = Infallible;
type RecordIter<'a> = std::vec::IntoIter<Result<CollectionRecord, Infallible>>;
fn records<'a>(&'a self) -> Result<Self::RecordIter<'a>, Self::RecordsError> {
self.records_calls.set(self.records_calls.get() + 1);
Ok(Vec::new().into_iter())
}
fn collections(&self) -> Result<Vec<CollectionHandle>, Self::RecordsError> {
Ok(Vec::new())
}
fn select_records(
&self,
_selectors: &BTreeSet<CollectionRecordSelector>,
) -> Result<Vec<CollectionRecord>, Self::RecordsError> {
self.selection_calls.set(self.selection_calls.get() + 1);
Ok(Vec::new())
}
}
impl CollectionStore for OverrideStore {
type InsertError = Infallible;
fn insert(&mut self, _record: CollectionRecord) -> Result<(), Self::InsertError> {
Ok(())
}
}
#[test]
fn shared_reference_forwards_selection_override() {
let store = OverrideStore::default();
let borrowed = &store;
let selectors = [CollectionRecordSelector::Collection(collection(1))]
.into_iter()
.collect();
CollectionRead::select_records(&borrowed, &selectors).unwrap();
assert_eq!(store.selection_calls.get(), 1);
assert_eq!(store.records_calls.get(), 0);
}
}