use crate::blob::{encodings::simplearchive::SimpleArchive, IntoBlob};
use crate::capability::capability_action;
use crate::prelude::entity;
use crate::trible::Fragment;
pub use crate::capability::policy::{
AdmissionPolicy, AdmissionPolicyError, ValidatedQuorum, KIND_ADMISSION_POLICY_OPEN,
KIND_ADMISSION_POLICY_QUORUM,
};
use super::{ACTION_READ, ACTION_WRITE};
pub(super) fn read_definition() -> Fragment {
entity! { capability_action: ACTION_READ }
}
pub(super) fn write_definition() -> Fragment {
entity! { capability_action: ACTION_WRITE }
}
#[derive(Clone, Debug)]
pub struct CollectionPolicy {
read: AdmissionPolicy,
write: AdmissionPolicy,
bindings: Fragment,
}
impl PartialEq for CollectionPolicy {
fn eq(&self, other: &Self) -> bool {
self.read == other.read
&& self.write == other.write
&& self.bindings.facts() == other.bindings.facts()
}
}
impl Eq for CollectionPolicy {}
impl CollectionPolicy {
pub fn new(read: AdmissionPolicy, write: AdmissionPolicy) -> Self {
let bindings =
bind_definition(read_definition(), &read) + bind_definition(write_definition(), &write);
Self {
read,
write,
bindings,
}
}
pub fn with_capability(mut self, definition: Fragment, policy: AdmissionPolicy) -> Self {
self.bindings += bind_definition(definition, &policy);
self
}
pub fn fragment(&self) -> Fragment {
self.bindings.clone()
}
pub const fn read(&self) -> &AdmissionPolicy {
&self.read
}
pub const fn write(&self) -> &AdmissionPolicy {
&self.write
}
pub(crate) fn from_bindings(
read: AdmissionPolicy,
write: AdmissionPolicy,
mut bindings: Fragment,
) -> Self {
bindings.put::<SimpleArchive, _>(read_definition().facts().clone());
bindings.put::<SimpleArchive, _>(write_definition().facts().clone());
Self {
read,
write,
bindings,
}
}
}
fn bind_definition(definition: Fragment, policy: &AdmissionPolicy) -> Fragment {
let (_, facts, metafacts, blobs) = definition.into_parts();
let blob = IntoBlob::<SimpleArchive>::to_blob(facts);
let mut binding = policy.binding(blob.get_handle());
*binding.metafacts_mut() += metafacts;
binding.blobs_mut().union(blobs);
binding.put::<SimpleArchive, _>(blob);
binding
}
#[cfg(test)]
mod tests {
use super::*;
use crate::capability::policy::{capability_handle, resource_policy};
use crate::collection::{descriptor, read_capability, write_capability};
use crate::metadata;
use crate::prelude::{find, pattern};
use crate::repo::{BlobStoreGet, SnapshotSource};
#[test]
fn generic_binding_construction_is_idempotent_and_preserves_definition_blobs() {
let definition = entity! { metadata::name: "custom key delivery" };
let handle = IntoBlob::<SimpleArchive>::to_blob(definition.facts().clone()).get_handle();
let once = CollectionPolicy::new(AdmissionPolicy::Open, AdmissionPolicy::Open)
.with_capability(definition.clone(), AdmissionPolicy::Open);
let twice = once
.clone()
.with_capability(definition, AdmissionPolicy::Open);
assert_eq!(once, twice);
let fragment = descriptor::naming::<SimpleArchive>("capabilities", once);
let handles: std::collections::BTreeSet<_> = find!(
handle: crate::capability::CapabilityHandle,
pattern!(fragment.facts(), [
{ _?descriptor @ resource_policy: _?binding },
{ _?binding @ capability_handle: ?handle },
])
)
.collect();
assert_eq!(
handles,
[read_capability(), write_capability(), handle].into()
);
let blobs = fragment.blobs().clone().snapshot().unwrap();
for handle in handles {
assert!(blobs
.get::<crate::blob::Blob<SimpleArchive>, _>(handle)
.is_ok());
}
}
}