use std::{cell::RefCell, collections::HashSet};
use bc_components::{Digest, DigestProvider};
use super::{envelope::EnvelopeCase, walk::EdgeType};
use crate::Envelope;
impl DigestProvider for Envelope {
fn digest(&self) -> Digest {
match self.case() {
EnvelopeCase::Node { digest, .. } => *digest,
EnvelopeCase::Leaf { digest, .. } => *digest,
EnvelopeCase::Wrapped { digest, .. } => *digest,
EnvelopeCase::Assertion(assertion) => assertion.digest(),
EnvelopeCase::Elided(digest) => *digest,
#[cfg(feature = "known_value")]
EnvelopeCase::KnownValue { digest, .. } => *digest,
#[cfg(feature = "encrypt")]
EnvelopeCase::Encrypted(encrypted_message) => {
encrypted_message.digest()
}
#[cfg(feature = "compress")]
EnvelopeCase::Compressed(compressed) => compressed.digest(),
}
}
}
impl Envelope {
pub fn digests(&self, level_limit: usize) -> HashSet<Digest> {
let result = RefCell::new(HashSet::new());
let visitor = |envelope: &Envelope,
level: usize,
_: EdgeType,
_: ()|
-> (_, bool) {
if level < level_limit {
let mut result = result.borrow_mut();
result.insert(envelope.digest());
result.insert(envelope.subject().digest());
}
((), false) };
self.walk(false, (), &visitor);
result.into_inner()
}
pub fn deep_digests(&self) -> HashSet<Digest> { self.digests(usize::MAX) }
pub fn shallow_digests(&self) -> HashSet<Digest> { self.digests(2) }
pub fn structural_digest(&self) -> Digest {
let image = RefCell::new(Vec::new());
let visitor =
|envelope: &Envelope, _: usize, _: EdgeType, _: ()| -> (_, bool) {
match envelope.case() {
EnvelopeCase::Elided(_) => image.borrow_mut().push(1),
#[cfg(feature = "encrypt")]
EnvelopeCase::Encrypted(_) => image.borrow_mut().push(0),
#[cfg(feature = "compress")]
EnvelopeCase::Compressed(_) => image.borrow_mut().push(2),
_ => {}
}
image
.borrow_mut()
.extend_from_slice(envelope.digest().data());
((), false) };
self.walk(false, (), &visitor);
Digest::from_image(image.into_inner())
}
pub fn is_equivalent_to(&self, other: &Self) -> bool {
self.digest() == other.digest()
}
pub fn is_identical_to(&self, other: &Self) -> bool {
if !self.is_equivalent_to(other) {
return false; }
self.structural_digest() == other.structural_digest()
}
}
impl PartialEq for Envelope {
fn eq(&self, other: &Self) -> bool { self.is_identical_to(other) }
}