use core::marker::PhantomData;
use crate::state::{Admitted, EvidenceState, Exportable, Parsed, Projected, Raw, Receipted};
use crate::witness::{Ocel20, Xes1849};
pub type RawOcelEvidence<T> = Evidence<T, Raw, Ocel20>;
pub type AdmittedOcelEvidence<T> = Evidence<T, Admitted, Ocel20>;
pub type RawXesEvidence<T> = Evidence<T, Raw, Xes1849>;
pub type AdmittedXesEvidence<T> = Evidence<T, Admitted, Xes1849>;
pub type ReceiptedEvidence<T, W> = Evidence<T, Receipted, W>;
pub use crate::law::EvidenceMode;
#[doc(alias = "process evidence")]
#[doc(alias = "evidence carrier")]
pub struct Evidence<T, State: EvidenceState, W> {
pub value: T,
pub state: PhantomData<State>,
pub witness: PhantomData<W>,
}
impl<T, W> Evidence<T, Raw, W> {
#[inline]
pub const fn raw(value: T) -> Evidence<T, Raw, W> {
Evidence {
value,
state: PhantomData,
witness: PhantomData,
}
}
#[inline]
pub fn refuse(self) -> Evidence<T, crate::state::Refused, W> {
Evidence {
value: self.value,
state: PhantomData,
witness: PhantomData,
}
}
#[inline]
pub fn into_parsed(self) -> Evidence<T, Parsed, W> {
Evidence {
value: self.value,
state: PhantomData,
witness: PhantomData,
}
}
}
impl<T, W> Evidence<T, crate::state::Parsed, W> {
#[inline]
pub fn into_refused(self) -> Evidence<T, crate::state::Refused, W> {
Evidence {
value: self.value,
state: core::marker::PhantomData,
witness: core::marker::PhantomData,
}
}
}
impl<T, W> Evidence<T, crate::state::Admitted, W> {
#[inline]
pub(crate) fn sealed(value: T) -> Evidence<T, Admitted, W> {
Evidence {
value,
state: PhantomData,
witness: PhantomData,
}
}
#[inline]
pub fn into_inner(self) -> T {
self.value
}
#[inline]
pub fn into_exportable(self) -> Evidence<T, crate::state::Exportable, W> {
Evidence {
value: self.value,
state: core::marker::PhantomData,
witness: core::marker::PhantomData,
}
}
#[inline]
pub fn into_receipted(self) -> Evidence<T, crate::state::Receipted, W> {
Evidence {
value: self.value,
state: core::marker::PhantomData,
witness: core::marker::PhantomData,
}
}
#[inline]
pub fn into_projected(self) -> Evidence<T, Projected, W> {
Evidence {
value: self.value,
state: PhantomData,
witness: PhantomData,
}
}
}
impl<T, W> Evidence<T, Projected, W> {
#[inline]
pub fn into_exportable(self) -> Evidence<T, Exportable, W> {
Evidence {
value: self.value,
state: PhantomData,
witness: PhantomData,
}
}
#[inline]
pub fn into_receipted(self) -> Evidence<T, Receipted, W> {
Evidence {
value: self.value,
state: PhantomData,
witness: PhantomData,
}
}
}
impl<T, W> Evidence<T, crate::state::Refused, W> {
#[inline]
pub(crate) fn refused(value: T) -> Evidence<T, crate::state::Refused, W> {
Evidence {
value,
state: PhantomData,
witness: PhantomData,
}
}
#[inline]
pub fn as_refused_value(&self) -> &T {
&self.value
}
#[inline]
pub fn into_refused_value(self) -> T {
self.value
}
}
impl<T: core::fmt::Debug, State: EvidenceState + core::fmt::Debug, W> core::fmt::Debug
for Evidence<T, State, W>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Evidence")
.field("value", &self.value)
.field("state", &self.state)
.finish()
}
}
impl<T, W> Evidence<T, Exportable, W> {
#[inline]
pub fn into_receipted(self) -> Evidence<T, Receipted, W> {
Evidence {
value: self.value,
state: PhantomData,
witness: PhantomData,
}
}
}
#[cfg(test)]
mod state_transition_tests {
use super::*;
use crate::admission::Admission;
use crate::state::{Exportable, Projected, Receipted, Refused};
use crate::witness::Ocel20;
#[test]
fn full_lifecycle_raw_to_receipted_via_projected() {
let raw: Evidence<u32, Raw, Ocel20> = Evidence::raw(1u32);
let _parsed: Evidence<u32, Parsed, Ocel20> = raw.into_parsed();
let admitted: Evidence<u32, Admitted, Ocel20> = Admission::new(1u32).into_evidence();
let projected: Evidence<u32, Projected, Ocel20> = admitted.into_projected();
let receipted: Evidence<u32, Receipted, Ocel20> = projected.into_receipted();
assert_eq!(receipted.value, 1u32);
}
#[test]
fn admitted_to_exportable_to_receipted() {
let admitted: Evidence<&str, Admitted, Ocel20> = Admission::new("payload").into_evidence();
let exportable: Evidence<&str, Exportable, Ocel20> = admitted.into_exportable();
let receipted: Evidence<&str, Receipted, Ocel20> = exportable.into_receipted();
assert_eq!(receipted.value, "payload");
}
#[test]
fn admitted_directly_to_receipted() {
let admitted: Evidence<u8, Admitted, Ocel20> = Admission::new(42u8).into_evidence();
let receipted: Evidence<u8, Receipted, Ocel20> = admitted.into_receipted();
assert_eq!(receipted.value, 42u8);
}
#[test]
fn parsed_to_refused_carries_value() {
let refused: Evidence<&str, Refused, Ocel20> = Evidence::<_, _, Ocel20>::raw("malformed")
.into_parsed()
.into_refused();
assert_eq!(refused.as_refused_value(), &"malformed");
assert_eq!(refused.into_refused_value(), "malformed");
}
#[test]
fn projected_to_exportable() {
let ev: Evidence<u32, Exportable, Ocel20> = Admission::<_, Ocel20>::new(7u32)
.into_evidence()
.into_projected()
.into_exportable();
assert_eq!(ev.value, 7u32);
}
#[test]
fn refused_constructor_carries_value_for_diagnostics() {
let refused = Evidence::<_, Refused, Ocel20>::refused("boundary-reject");
assert_eq!(*refused.as_refused_value(), "boundary-reject");
}
}