lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

admission.rs (5390B)


      1 use core::fmt;
      2 
      3 use radroots_event::{contract::EventContract, envelope::EventEnvelope};
      4 
      5 use crate::{
      6     post::inbound::{
      7         RadrootsInboundPostProjection, RadrootsPostProjectionError, project_verified_post_event,
      8     },
      9     verification::{
     10         RadrootsNip01VerificationError, RadrootsSignatureVerifiedEvent, verify_nip01_event,
     11     },
     12 };
     13 
     14 /// A signature-and-id verified root kind-1 event bound to a product projection.
     15 #[derive(Clone, Debug, PartialEq, Eq)]
     16 pub struct RadrootsAdmittedRootPostEvent {
     17     verified_event: RadrootsSignatureVerifiedEvent,
     18     projection: RadrootsInboundPostProjection,
     19 }
     20 
     21 impl RadrootsAdmittedRootPostEvent {
     22     pub fn verified_event(&self) -> &RadrootsSignatureVerifiedEvent {
     23         &self.verified_event
     24     }
     25 
     26     pub fn event(&self) -> &EventEnvelope {
     27         self.verified_event.event()
     28     }
     29 
     30     pub fn projection(&self) -> &RadrootsInboundPostProjection {
     31         &self.projection
     32     }
     33 
     34     pub fn contract(&self) -> &'static EventContract {
     35         debug_assert!(self.projection.classification().is_root_card());
     36         radroots_event::contract::event_contract(self.projection.classification().contract_id())
     37             .expect("root post projection contract IDs are registry-owned")
     38     }
     39 
     40     pub fn into_parts(
     41         self,
     42     ) -> (
     43         RadrootsSignatureVerifiedEvent,
     44         RadrootsInboundPostProjection,
     45     ) {
     46         (self.verified_event, self.projection)
     47     }
     48 }
     49 
     50 /// A verified kind-1 event excluded from root-card admission by an `e` tag.
     51 ///
     52 /// This candidate carries no Reply claim. Promotion into a semantic thread
     53 /// model is available only through the dedicated NIP-10 admission boundary.
     54 #[derive(Clone, Debug, PartialEq, Eq)]
     55 pub struct RadrootsThreadExcludedPostCandidate {
     56     verified_event: RadrootsSignatureVerifiedEvent,
     57     projection: RadrootsInboundPostProjection,
     58 }
     59 
     60 impl RadrootsThreadExcludedPostCandidate {
     61     pub fn verified_event(&self) -> &RadrootsSignatureVerifiedEvent {
     62         &self.verified_event
     63     }
     64 
     65     pub fn event(&self) -> &EventEnvelope {
     66         self.verified_event.event()
     67     }
     68 
     69     pub fn projection(&self) -> &RadrootsInboundPostProjection {
     70         &self.projection
     71     }
     72 
     73     pub fn into_parts(
     74         self,
     75     ) -> (
     76         RadrootsSignatureVerifiedEvent,
     77         RadrootsInboundPostProjection,
     78     ) {
     79         (self.verified_event, self.projection)
     80     }
     81 }
     82 
     83 /// Result of verifying a kind-1 event at the root-post admission boundary.
     84 #[non_exhaustive]
     85 #[derive(Clone, Debug, PartialEq, Eq)]
     86 pub enum RadrootsPostAdmissionOutcome {
     87     Root(RadrootsAdmittedRootPostEvent),
     88     ThreadExcluded(RadrootsThreadExcludedPostCandidate),
     89 }
     90 
     91 #[non_exhaustive]
     92 #[derive(Clone, Debug, PartialEq, Eq)]
     93 pub enum RadrootsPostAdmissionError {
     94     Nip01Verification(RadrootsNip01VerificationError),
     95     Projection(RadrootsPostProjectionError),
     96 }
     97 
     98 impl RadrootsPostAdmissionError {
     99     pub const fn code(&self) -> &'static str {
    100         match self {
    101             Self::Nip01Verification(error) => error.code(),
    102             Self::Projection(error) => error.code(),
    103         }
    104     }
    105 }
    106 
    107 impl fmt::Display for RadrootsPostAdmissionError {
    108     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    109         match self {
    110             Self::Nip01Verification(error) => write!(formatter, "{error}"),
    111             Self::Projection(error) => write!(formatter, "{error}"),
    112         }
    113     }
    114 }
    115 
    116 #[cfg(feature = "std")]
    117 impl std::error::Error for RadrootsPostAdmissionError {
    118     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
    119         match self {
    120             Self::Nip01Verification(error) => Some(error),
    121             Self::Projection(error) => Some(error),
    122         }
    123     }
    124 }
    125 
    126 impl From<RadrootsNip01VerificationError> for RadrootsPostAdmissionError {
    127     fn from(value: RadrootsNip01VerificationError) -> Self {
    128         Self::Nip01Verification(value)
    129     }
    130 }
    131 
    132 impl From<RadrootsPostProjectionError> for RadrootsPostAdmissionError {
    133     fn from(value: RadrootsPostProjectionError) -> Self {
    134         Self::Projection(value)
    135     }
    136 }
    137 
    138 /// Classifies an already verified kind-1 event at the product post boundary.
    139 ///
    140 /// Root post profiles are admitted as product posts. Thread-shaped candidates
    141 /// are returned separately and do not establish a root post or reply contract.
    142 pub fn admit_verified_post_event(
    143     verified_event: RadrootsSignatureVerifiedEvent,
    144 ) -> Result<RadrootsPostAdmissionOutcome, RadrootsPostAdmissionError> {
    145     let projection = project_verified_post_event(&verified_event)?;
    146     if projection.classification().is_root_card() {
    147         Ok(RadrootsPostAdmissionOutcome::Root(
    148             RadrootsAdmittedRootPostEvent {
    149                 verified_event,
    150                 projection,
    151             },
    152         ))
    153     } else {
    154         Ok(RadrootsPostAdmissionOutcome::ThreadExcluded(
    155             RadrootsThreadExcludedPostCandidate {
    156                 verified_event,
    157                 projection,
    158             },
    159         ))
    160     }
    161 }
    162 
    163 /// Verifies NIP-01 state, admitting only root profiles as product posts.
    164 ///
    165 /// A verified thread candidate is returned as thread-excluded compatibility
    166 /// data; this boundary does not claim that it is a valid Radroots reply.
    167 pub fn verify_and_admit_post_event(
    168     event: EventEnvelope,
    169 ) -> Result<RadrootsPostAdmissionOutcome, RadrootsPostAdmissionError> {
    170     admit_verified_post_event(verify_nip01_event(event)?)
    171 }