lib

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

model.rs (11670B)


      1 #![forbid(unsafe_code)]
      2 
      3 use crate::RadrootsOutboxError;
      4 use radroots_events::draft::{RadrootsFrozenEventDraft, RadrootsSignedNostrEvent};
      5 
      6 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
      7 pub enum RadrootsOutboxOperationStatus {
      8     Queued,
      9     Complete,
     10     FailedTerminal,
     11     Cancelled,
     12 }
     13 
     14 impl RadrootsOutboxOperationStatus {
     15     pub fn as_str(self) -> &'static str {
     16         match self {
     17             Self::Queued => "queued",
     18             Self::Complete => "complete",
     19             Self::FailedTerminal => "failed_terminal",
     20             Self::Cancelled => "cancelled",
     21         }
     22     }
     23 
     24     pub fn parse(value: &str) -> Result<Self, RadrootsOutboxError> {
     25         match value {
     26             "queued" => Ok(Self::Queued),
     27             "complete" => Ok(Self::Complete),
     28             "failed_terminal" => Ok(Self::FailedTerminal),
     29             "cancelled" => Ok(Self::Cancelled),
     30             _ => Err(RadrootsOutboxError::InvalidStoredEnum {
     31                 field: "outbox_operation.status",
     32                 value: value.to_owned(),
     33             }),
     34         }
     35     }
     36 }
     37 
     38 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
     39 pub enum RadrootsOutboxEventState {
     40     DraftQueued,
     41     Signing,
     42     Signed,
     43     Publishing,
     44     Published,
     45     SignRetryable,
     46     PublishRetryable,
     47     FailedTerminal,
     48     Cancelled,
     49 }
     50 
     51 impl RadrootsOutboxEventState {
     52     pub fn as_str(self) -> &'static str {
     53         match self {
     54             Self::DraftQueued => "draft_queued",
     55             Self::Signing => "signing",
     56             Self::Signed => "signed",
     57             Self::Publishing => "publishing",
     58             Self::Published => "published",
     59             Self::SignRetryable => "sign_retryable",
     60             Self::PublishRetryable => "publish_retryable",
     61             Self::FailedTerminal => "failed_terminal",
     62             Self::Cancelled => "cancelled",
     63         }
     64     }
     65 
     66     pub fn parse(value: &str) -> Result<Self, RadrootsOutboxError> {
     67         match value {
     68             "draft_queued" => Ok(Self::DraftQueued),
     69             "signing" => Ok(Self::Signing),
     70             "signed" => Ok(Self::Signed),
     71             "publishing" => Ok(Self::Publishing),
     72             "published" => Ok(Self::Published),
     73             "sign_retryable" => Ok(Self::SignRetryable),
     74             "publish_retryable" => Ok(Self::PublishRetryable),
     75             "failed_terminal" => Ok(Self::FailedTerminal),
     76             "cancelled" => Ok(Self::Cancelled),
     77             _ => Err(RadrootsOutboxError::InvalidStoredEnum {
     78                 field: "outbox_event.state",
     79                 value: value.to_owned(),
     80             }),
     81         }
     82     }
     83 
     84     pub fn is_terminal(self) -> bool {
     85         matches!(
     86             self,
     87             Self::Published | Self::FailedTerminal | Self::Cancelled
     88         )
     89     }
     90 }
     91 
     92 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
     93 pub enum RadrootsOutboxRelayStatus {
     94     Pending,
     95     Accepted,
     96     FailedRetryable,
     97     FailedTerminal,
     98 }
     99 
    100 impl RadrootsOutboxRelayStatus {
    101     pub fn as_str(self) -> &'static str {
    102         match self {
    103             Self::Pending => "pending",
    104             Self::Accepted => "accepted",
    105             Self::FailedRetryable => "failed_retryable",
    106             Self::FailedTerminal => "failed_terminal",
    107         }
    108     }
    109 
    110     pub fn parse(value: &str) -> Result<Self, RadrootsOutboxError> {
    111         match value {
    112             "pending" => Ok(Self::Pending),
    113             "accepted" => Ok(Self::Accepted),
    114             "failed_retryable" => Ok(Self::FailedRetryable),
    115             "failed_terminal" => Ok(Self::FailedTerminal),
    116             _ => Err(RadrootsOutboxError::InvalidStoredEnum {
    117                 field: "outbox_event_relay_status.status",
    118                 value: value.to_owned(),
    119             }),
    120         }
    121     }
    122 }
    123 
    124 #[derive(Clone, Debug, PartialEq, Eq)]
    125 pub struct RadrootsOutboxOperationInput {
    126     pub operation_kind: String,
    127     pub draft: RadrootsFrozenEventDraft,
    128     pub target_relays: Vec<String>,
    129     pub idempotency_key: Option<String>,
    130     pub allow_empty_target_relays: bool,
    131     pub created_at_ms: i64,
    132 }
    133 
    134 impl RadrootsOutboxOperationInput {
    135     pub fn new(
    136         operation_kind: impl Into<String>,
    137         draft: RadrootsFrozenEventDraft,
    138         target_relays: Vec<String>,
    139         created_at_ms: i64,
    140     ) -> Self {
    141         Self {
    142             operation_kind: operation_kind.into(),
    143             draft,
    144             target_relays,
    145             idempotency_key: None,
    146             allow_empty_target_relays: false,
    147             created_at_ms,
    148         }
    149     }
    150 
    151     pub fn with_idempotency_key(mut self, idempotency_key: impl Into<String>) -> Self {
    152         self.idempotency_key = Some(idempotency_key.into());
    153         self
    154     }
    155 
    156     pub fn allow_empty_target_relays(mut self) -> Self {
    157         self.allow_empty_target_relays = true;
    158         self
    159     }
    160 }
    161 
    162 #[derive(Clone, Debug, PartialEq, Eq)]
    163 pub struct RadrootsOutboxSignedOperationInput {
    164     pub operation_kind: String,
    165     pub draft: RadrootsFrozenEventDraft,
    166     pub signed_event: RadrootsSignedNostrEvent,
    167     pub target_relays: Vec<String>,
    168     pub idempotency_key: Option<String>,
    169     pub allow_empty_target_relays: bool,
    170     pub event_store_inserted: bool,
    171     pub event_store_ingested_at_ms: i64,
    172     pub created_at_ms: i64,
    173 }
    174 
    175 impl RadrootsOutboxSignedOperationInput {
    176     pub fn new(
    177         operation_kind: impl Into<String>,
    178         draft: RadrootsFrozenEventDraft,
    179         signed_event: RadrootsSignedNostrEvent,
    180         target_relays: Vec<String>,
    181         event_store_inserted: bool,
    182         event_store_ingested_at_ms: i64,
    183         created_at_ms: i64,
    184     ) -> Self {
    185         Self {
    186             operation_kind: operation_kind.into(),
    187             draft,
    188             signed_event,
    189             target_relays,
    190             idempotency_key: None,
    191             allow_empty_target_relays: false,
    192             event_store_inserted,
    193             event_store_ingested_at_ms,
    194             created_at_ms,
    195         }
    196     }
    197 
    198     pub fn with_idempotency_key(mut self, idempotency_key: impl Into<String>) -> Self {
    199         self.idempotency_key = Some(idempotency_key.into());
    200         self
    201     }
    202 
    203     pub fn allow_empty_target_relays(mut self) -> Self {
    204         self.allow_empty_target_relays = true;
    205         self
    206     }
    207 }
    208 
    209 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    210 pub enum RadrootsOutboxEnqueueStatus {
    211     Inserted,
    212     Existing,
    213 }
    214 
    215 #[derive(Clone, Debug, PartialEq, Eq)]
    216 pub struct RadrootsOutboxEnqueueReceipt {
    217     pub status: RadrootsOutboxEnqueueStatus,
    218     pub operation_id: i64,
    219     pub outbox_event_id: i64,
    220     pub expected_event_id: String,
    221     pub idempotency_digest: String,
    222 }
    223 
    224 #[derive(Clone, Debug, PartialEq, Eq)]
    225 pub struct RadrootsOutboxOperationRecord {
    226     pub operation_id: i64,
    227     pub operation_kind: String,
    228     pub expected_pubkey: String,
    229     pub idempotency_key: Option<String>,
    230     pub idempotency_digest: String,
    231     pub status: RadrootsOutboxOperationStatus,
    232     pub created_at_ms: i64,
    233     pub updated_at_ms: i64,
    234 }
    235 
    236 #[derive(Clone, Debug, PartialEq, Eq)]
    237 pub struct RadrootsOutboxEventRecord {
    238     pub outbox_event_id: i64,
    239     pub operation_id: i64,
    240     pub event_id: String,
    241     pub expected_pubkey: String,
    242     pub draft: RadrootsFrozenEventDraft,
    243     pub signed_event: Option<RadrootsSignedNostrEvent>,
    244     pub raw_event_json: Option<String>,
    245     pub state: RadrootsOutboxEventState,
    246     pub accepted_quorum: i64,
    247     pub attempt_count: i64,
    248     pub claim_token: Option<String>,
    249     pub claim_owner: Option<String>,
    250     pub claim_expires_at_ms: Option<i64>,
    251     pub next_attempt_after_ms: i64,
    252     pub last_error: Option<String>,
    253     pub event_store_ingested: bool,
    254     pub event_store_inserted: bool,
    255     pub event_store_ingested_at_ms: Option<i64>,
    256     pub created_at_ms: i64,
    257     pub updated_at_ms: i64,
    258 }
    259 
    260 #[derive(Clone, Debug, PartialEq, Eq)]
    261 pub struct RadrootsOutboxRelayStatusRecord {
    262     pub outbox_event_id: i64,
    263     pub relay_url: String,
    264     pub status: RadrootsOutboxRelayStatus,
    265     pub attempt_count: i64,
    266     pub last_attempt_at_ms: Option<i64>,
    267     pub acknowledged_at_ms: Option<i64>,
    268     pub last_error: Option<String>,
    269 }
    270 
    271 #[derive(Clone, Debug, PartialEq, Eq)]
    272 pub struct RadrootsOutboxClaimedEvent {
    273     pub outbox_event_id: i64,
    274     pub operation_id: i64,
    275     pub expected_event_id: String,
    276     pub attempt_count: i64,
    277     pub state: RadrootsOutboxEventState,
    278     pub claim_token: String,
    279     pub draft: RadrootsFrozenEventDraft,
    280     pub signed_event: Option<RadrootsSignedNostrEvent>,
    281     pub target_relays: Vec<String>,
    282 }
    283 
    284 #[derive(Clone, Debug, PartialEq, Eq)]
    285 pub struct RadrootsOutboxEventStoreIngestReceipt {
    286     pub outbox_event_id: i64,
    287     pub event_id: String,
    288     pub already_ingested: bool,
    289     pub event_store_inserted: bool,
    290 }
    291 
    292 #[derive(Clone, Debug, PartialEq, Eq)]
    293 pub struct RadrootsOutboxStatusSummary {
    294     pub total_events: i64,
    295     pub pending_events: i64,
    296     pub retryable_events: i64,
    297     pub terminal_events: i64,
    298     pub failed_terminal_events: i64,
    299     pub ready_signed_events: i64,
    300     pub publishing_events: i64,
    301     pub last_attempt_at_ms: Option<i64>,
    302     pub last_error: Option<String>,
    303 }
    304 
    305 #[cfg(test)]
    306 mod tests {
    307     use super::*;
    308 
    309     #[test]
    310     fn operation_event_and_relay_status_values_round_trip() {
    311         for (status, expected) in [
    312             (RadrootsOutboxOperationStatus::Queued, "queued"),
    313             (RadrootsOutboxOperationStatus::Complete, "complete"),
    314             (
    315                 RadrootsOutboxOperationStatus::FailedTerminal,
    316                 "failed_terminal",
    317             ),
    318             (RadrootsOutboxOperationStatus::Cancelled, "cancelled"),
    319         ] {
    320             assert_eq!(status.as_str(), expected);
    321             assert_eq!(
    322                 RadrootsOutboxOperationStatus::parse(expected).expect("status"),
    323                 status
    324             );
    325         }
    326         assert!(RadrootsOutboxOperationStatus::parse("bad").is_err());
    327 
    328         for (state, expected, terminal) in [
    329             (RadrootsOutboxEventState::DraftQueued, "draft_queued", false),
    330             (RadrootsOutboxEventState::Signing, "signing", false),
    331             (RadrootsOutboxEventState::Signed, "signed", false),
    332             (RadrootsOutboxEventState::Publishing, "publishing", false),
    333             (RadrootsOutboxEventState::Published, "published", true),
    334             (
    335                 RadrootsOutboxEventState::SignRetryable,
    336                 "sign_retryable",
    337                 false,
    338             ),
    339             (
    340                 RadrootsOutboxEventState::PublishRetryable,
    341                 "publish_retryable",
    342                 false,
    343             ),
    344             (
    345                 RadrootsOutboxEventState::FailedTerminal,
    346                 "failed_terminal",
    347                 true,
    348             ),
    349             (RadrootsOutboxEventState::Cancelled, "cancelled", true),
    350         ] {
    351             assert_eq!(state.as_str(), expected);
    352             assert_eq!(
    353                 RadrootsOutboxEventState::parse(expected).expect("state"),
    354                 state
    355             );
    356             assert_eq!(state.is_terminal(), terminal);
    357         }
    358         assert!(RadrootsOutboxEventState::parse("bad").is_err());
    359 
    360         for (status, expected) in [
    361             (RadrootsOutboxRelayStatus::Pending, "pending"),
    362             (RadrootsOutboxRelayStatus::Accepted, "accepted"),
    363             (
    364                 RadrootsOutboxRelayStatus::FailedRetryable,
    365                 "failed_retryable",
    366             ),
    367             (RadrootsOutboxRelayStatus::FailedTerminal, "failed_terminal"),
    368         ] {
    369             assert_eq!(status.as_str(), expected);
    370             assert_eq!(
    371                 RadrootsOutboxRelayStatus::parse(expected).expect("relay status"),
    372                 status
    373             );
    374         }
    375         assert!(RadrootsOutboxRelayStatus::parse("bad").is_err());
    376     }
    377 }