use content_addressable::{ContentAddressable, ContentError, ContentId, RawContentId};
use serde::{Deserialize, Serialize};
use crate::derivation::{Derivation, Span};
use crate::op::Op;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Life {
Live,
Superseded,
Withdrawn,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum SealError {
#[error(
"v0 mints elision only: `{op:?}` has verification class `{check:?}`, which has \
no implementation and no calibrated threshold. Minting it would produce a unit \
whose check nobody can run."
)]
NotMintableInV0 {
op: Op,
check: crate::op::Check,
},
#[error("span [{start}, {end}) does not lie within a source of {source_len} bytes")]
SpanOutOfSource {
start: u64,
end: u64,
source_len: usize,
},
#[error(
"span [{start}, {end}) covers no bytes: a unit that addresses nothing cannot be re-derived"
)]
UnaddressedSpan {
start: u64,
end: u64,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum AdmitError {
#[error(
"decoded unit declares `{op:?}` (verification class `{check:?}`), which v0 does not \
admit: its check has no implementation, so the unit would be unverifiable evidence"
)]
NotMintableInV0 {
op: Op,
check: crate::op::Check,
},
#[error(
"decoded unit declares depth {declared}, but `{op:?}` over source material implies \
depth {implied}. This is the check the old `Deserialize` derive bypassed."
)]
DepthMismatch {
op: Op,
declared: u32,
implied: u32,
},
#[error("decoded unit's span [{start}, {end}) covers no bytes and so addresses nothing")]
UnaddressedSpan {
start: u64,
end: u64,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RawUnit {
pub op: Op,
pub source: RawContentId,
pub span: Span,
pub elided: RawContentId,
pub root: ContentId,
pub depth: u32,
pub life: Life,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Unit {
derivation: Derivation,
depth: u32,
life: Life,
}
impl Serialize for Unit {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
self.to_raw().serialize(s)
}
}
impl Unit {
pub fn seal(op: Op, source: &[u8], span: Span, root: ContentId) -> Result<Self, SealError> {
if op != Op::Elide {
return Err(SealError::NotMintableInV0 {
op,
check: op.check(),
});
}
if span.is_empty() {
return Err(SealError::UnaddressedSpan {
start: span.start,
end: span.end,
});
}
let derivation =
Derivation::over(op, source, span, root).ok_or(SealError::SpanOutOfSource {
start: span.start,
end: span.end,
source_len: source.len(),
})?;
Ok(Self {
derivation,
depth: op.depth_after(0),
life: Life::Live,
})
}
#[must_use]
pub const fn derivation(&self) -> &Derivation {
&self.derivation
}
pub fn id(&self) -> Result<crate::UnitId, ContentError> {
self.derivation.id().map(crate::UnitId::from)
}
#[must_use]
pub const fn op(&self) -> Op {
self.derivation.op
}
#[must_use]
pub const fn depth(&self) -> u32 {
self.depth
}
#[must_use]
pub const fn root(&self) -> ContentId {
self.derivation.root
}
#[must_use]
pub const fn life(&self) -> Life {
self.life
}
#[must_use]
pub const fn is_well_formed(&self) -> bool {
self.depth <= 1
}
#[must_use]
pub const fn evictable(&self) -> bool {
match self.life {
Life::Live => false,
Life::Superseded | Life::Withdrawn => true,
}
}
#[must_use]
pub const fn with_life(mut self, life: Life) -> Self {
self.life = life;
self
}
#[must_use]
pub const fn to_raw(&self) -> RawUnit {
RawUnit {
op: self.derivation.op,
source: self.derivation.source,
span: self.derivation.span,
elided: self.derivation.elided,
root: self.derivation.root,
depth: self.depth,
life: self.life,
}
}
}
impl TryFrom<RawUnit> for Unit {
type Error = AdmitError;
fn try_from(raw: RawUnit) -> Result<Self, Self::Error> {
if raw.op != Op::Elide {
return Err(AdmitError::NotMintableInV0 {
op: raw.op,
check: raw.op.check(),
});
}
let implied = raw.op.depth_after(0);
if raw.depth != implied {
return Err(AdmitError::DepthMismatch {
op: raw.op,
declared: raw.depth,
implied,
});
}
if raw.span.is_empty() {
return Err(AdmitError::UnaddressedSpan {
start: raw.span.start,
end: raw.span.end,
});
}
Ok(Self {
derivation: Derivation {
op: raw.op,
source: raw.source,
span: raw.span,
elided: raw.elided,
root: raw.root,
},
depth: raw.depth,
life: raw.life,
})
}
}
impl ContentAddressable for Unit {
fn canonical_form(&self) -> Result<Vec<u8>, ContentError> {
self.derivation.canonical_form()
}
}