field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

invalidation.rs (6914B)


      1 //! Versioned owned notification DTOs; durable records remain query-owned.
      2 
      3 use tera_core::runtime::invalidation::{InvalidationDomain, RuntimeInvalidation};
      4 
      5 use crate::FfiLocalNetworkRecord;
      6 
      7 pub const RUNTIME_CHANGE_SCHEMA_VERSION: u16 = 3;
      8 
      9 /// A gap invalidates all query domains in the authenticated runtime scope.
     10 /// Its revision is not a watermark and must not suppress a final resnapshot.
     11 #[derive(Clone, Copy, Debug, Eq, PartialEq, uniffi::Enum)]
     12 pub enum FfiRuntimeChangeDelivery {
     13     Change,
     14     ResnapshotRequired,
     15 }
     16 
     17 #[derive(Clone, Copy, Debug, Eq, PartialEq, uniffi::Enum)]
     18 pub enum FfiRuntimeChangeKind {
     19     Initial,
     20     Identity,
     21     Settings,
     22     Profile,
     23     Today,
     24     Drafts,
     25     Relay,
     26     Media,
     27     Lifecycle,
     28 }
     29 
     30 #[derive(Clone, Debug, Eq, PartialEq, uniffi::Record)]
     31 pub struct FfiRuntimeChangeScope {
     32     pub public_key: String,
     33     pub source_generation: String,
     34     pub context: Option<FfiLocalNetworkRecord>,
     35 }
     36 
     37 #[derive(Clone, Copy, Debug, Eq, PartialEq, uniffi::Enum)]
     38 pub enum FfiInvalidationRevision {
     39     Current { value: u64 },
     40     Exhausted,
     41 }
     42 
     43 #[derive(Clone, Debug, Eq, PartialEq, uniffi::Record)]
     44 pub struct FfiRuntimeChangeRecord {
     45     pub schema_version: u16,
     46     pub scope: FfiRuntimeChangeScope,
     47     pub epoch: String,
     48     pub revision: FfiInvalidationRevision,
     49     pub delivery: FfiRuntimeChangeDelivery,
     50     pub kind: FfiRuntimeChangeKind,
     51     pub entity_id: Option<String>,
     52 }
     53 
     54 impl From<RuntimeInvalidation> for FfiRuntimeChangeRecord {
     55     fn from(value: RuntimeInvalidation) -> Self {
     56         Self {
     57             schema_version: RUNTIME_CHANGE_SCHEMA_VERSION,
     58             scope: FfiRuntimeChangeScope {
     59                 public_key: value.scope.public_key().to_hex(),
     60                 source_generation: hex::encode(value.scope.source_generation().as_bytes()),
     61                 context: value.scope.context().cloned().map(Into::into),
     62             },
     63             epoch: format!("{:032x}", value.epoch),
     64             revision: match value.revision.value() {
     65                 Some(value) => FfiInvalidationRevision::Current { value },
     66                 None => FfiInvalidationRevision::Exhausted,
     67             },
     68             delivery: FfiRuntimeChangeDelivery::Change,
     69             kind: value.domain.into(),
     70             entity_id: value.entity_id,
     71         }
     72     }
     73 }
     74 
     75 impl FfiRuntimeChangeRecord {
     76     pub(crate) fn requiring_resnapshot(mut self) -> Self {
     77         self.scope.context = None;
     78         self.kind = FfiRuntimeChangeKind::Initial;
     79         self.revision = FfiInvalidationRevision::Current { value: 0 };
     80         self.delivery = FfiRuntimeChangeDelivery::ResnapshotRequired;
     81         self.entity_id = None;
     82         self
     83     }
     84 }
     85 
     86 impl From<FfiRuntimeChangeKind> for InvalidationDomain {
     87     fn from(value: FfiRuntimeChangeKind) -> Self {
     88         match value {
     89             FfiRuntimeChangeKind::Initial => Self::Initial,
     90             FfiRuntimeChangeKind::Identity => Self::Identity,
     91             FfiRuntimeChangeKind::Settings => Self::Settings,
     92             FfiRuntimeChangeKind::Profile => Self::Profile,
     93             FfiRuntimeChangeKind::Today => Self::Today,
     94             FfiRuntimeChangeKind::Drafts => Self::Drafts,
     95             FfiRuntimeChangeKind::Relay => Self::Relay,
     96             FfiRuntimeChangeKind::Media => Self::Media,
     97             FfiRuntimeChangeKind::Lifecycle => Self::Lifecycle,
     98         }
     99     }
    100 }
    101 
    102 impl From<InvalidationDomain> for FfiRuntimeChangeKind {
    103     fn from(value: InvalidationDomain) -> Self {
    104         match value {
    105             InvalidationDomain::Initial => Self::Initial,
    106             InvalidationDomain::Identity => Self::Identity,
    107             InvalidationDomain::Settings => Self::Settings,
    108             InvalidationDomain::Profile => Self::Profile,
    109             InvalidationDomain::Today => Self::Today,
    110             InvalidationDomain::Drafts => Self::Drafts,
    111             InvalidationDomain::Relay => Self::Relay,
    112             InvalidationDomain::Media => Self::Media,
    113             InvalidationDomain::Lifecycle => Self::Lifecycle,
    114         }
    115     }
    116 }
    117 
    118 #[cfg(test)]
    119 mod tests {
    120     use super::*;
    121 
    122     #[test]
    123     fn all_domains_and_both_revision_states_round_trip_through_the_generated_codec() {
    124         for domain in [
    125             InvalidationDomain::Initial,
    126             InvalidationDomain::Identity,
    127             InvalidationDomain::Settings,
    128             InvalidationDomain::Profile,
    129             InvalidationDomain::Today,
    130             InvalidationDomain::Drafts,
    131             InvalidationDomain::Relay,
    132             InvalidationDomain::Media,
    133             InvalidationDomain::Lifecycle,
    134         ] {
    135             let kind = FfiRuntimeChangeKind::from(domain);
    136             assert_eq!(InvalidationDomain::from(kind), domain);
    137             for revision in [
    138                 FfiInvalidationRevision::Current { value: u64::MAX },
    139                 FfiInvalidationRevision::Exhausted,
    140             ] {
    141                 let record = FfiRuntimeChangeRecord {
    142                     schema_version: RUNTIME_CHANGE_SCHEMA_VERSION,
    143                     scope: FfiRuntimeChangeScope {
    144                         public_key: "a".repeat(64),
    145                         source_generation: "b".repeat(64),
    146                         context: Some(FfiLocalNetworkRecord {
    147                             schema_version: 1,
    148                             id: "local".into(),
    149                             label: "Market".into(),
    150                             relay_urls: vec!["wss://relay.example".into()],
    151                             locality: Some("Town".into()),
    152                             followed_authors: vec!["c".repeat(64)],
    153                             generation: u64::MAX,
    154                         }),
    155                     },
    156                     epoch: "d".repeat(32),
    157                     revision,
    158                     delivery: FfiRuntimeChangeDelivery::Change,
    159                     kind,
    160                     entity_id: Some("draft".into()),
    161                 };
    162                 for record in [record.clone(), record.requiring_resnapshot()] {
    163                     let mut bytes = Vec::new();
    164                     <FfiRuntimeChangeRecord as uniffi::FfiConverter<crate::UniFfiTag>>::write(
    165                         record.clone(),
    166                         &mut bytes,
    167                     );
    168                     let mut input = bytes.as_slice();
    169                     let decoded = <FfiRuntimeChangeRecord as uniffi::FfiConverter<
    170                         crate::UniFfiTag,
    171                     >>::try_read(&mut input)
    172                     .unwrap();
    173                     assert_eq!(decoded, record);
    174                     assert!(input.is_empty());
    175                     let mut truncated = &bytes[..bytes.len() - 1];
    176                     assert!(
    177                     <FfiRuntimeChangeRecord as uniffi::FfiConverter<crate::UniFfiTag>>::try_read(
    178                         &mut truncated
    179                     )
    180                     .is_err()
    181                 );
    182                 }
    183             }
    184         }
    185     }
    186 }