lib

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

types.rs (7967B)


      1 use alloc::string::String;
      2 #[cfg(feature = "nostr-client")]
      3 use radroots_nostr::prelude::{
      4     RadrootsNostrFilter, RadrootsNostrPublicKey, RadrootsNostrTimestamp, radroots_nostr_kind,
      5     radroots_nostr_post_events_filter,
      6 };
      7 
      8 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
      9 pub enum RadrootsNostrSubscriptionPolicy {
     10     Streaming,
     11     OneShotOnEose,
     12 }
     13 
     14 #[derive(Debug, Clone)]
     15 pub struct RadrootsNostrSubscriptionSpec {
     16     pub name: Option<String>,
     17     #[cfg(feature = "nostr-client")]
     18     pub filter: RadrootsNostrFilter,
     19     pub policy: RadrootsNostrSubscriptionPolicy,
     20     pub stream_timeout_secs: u64,
     21     pub reconnect_delay_millis: u64,
     22 }
     23 
     24 impl RadrootsNostrSubscriptionSpec {
     25     pub const DEFAULT_STREAM_TIMEOUT_SECS: u64 = 30;
     26     pub const DEFAULT_RECONNECT_DELAY_MILLIS: u64 = 2_000;
     27 
     28     #[cfg(feature = "nostr-client")]
     29     pub fn streaming(filter: RadrootsNostrFilter) -> Self {
     30         Self {
     31             name: None,
     32             filter,
     33             policy: RadrootsNostrSubscriptionPolicy::Streaming,
     34             stream_timeout_secs: Self::DEFAULT_STREAM_TIMEOUT_SECS,
     35             reconnect_delay_millis: Self::DEFAULT_RECONNECT_DELAY_MILLIS,
     36         }
     37     }
     38 
     39     #[cfg(feature = "nostr-client")]
     40     pub fn one_shot(filter: RadrootsNostrFilter) -> Self {
     41         Self {
     42             name: None,
     43             filter,
     44             policy: RadrootsNostrSubscriptionPolicy::OneShotOnEose,
     45             stream_timeout_secs: Self::DEFAULT_STREAM_TIMEOUT_SECS,
     46             reconnect_delay_millis: Self::DEFAULT_RECONNECT_DELAY_MILLIS,
     47         }
     48     }
     49 
     50     pub fn with_policy(mut self, policy: RadrootsNostrSubscriptionPolicy) -> Self {
     51         self.policy = policy;
     52         self
     53     }
     54 
     55     #[cfg(feature = "nostr-client")]
     56     pub fn text_notes(
     57         limit: Option<u16>,
     58         since_unix: Option<u64>,
     59         policy: RadrootsNostrSubscriptionPolicy,
     60     ) -> Self {
     61         Self::streaming(radroots_nostr_post_events_filter(limit, since_unix)).with_policy(policy)
     62     }
     63 
     64     #[cfg(feature = "nostr-client")]
     65     pub fn by_kind(
     66         kind: u16,
     67         limit: Option<u16>,
     68         since_unix: Option<u64>,
     69         policy: RadrootsNostrSubscriptionPolicy,
     70     ) -> Self {
     71         let mut filter = RadrootsNostrFilter::new().kind(radroots_nostr_kind(kind));
     72         if let Some(limit) = limit {
     73             filter = filter.limit(limit.into());
     74         }
     75         if let Some(since) = since_unix {
     76             filter = filter.since(RadrootsNostrTimestamp::from(since));
     77         }
     78         Self::streaming(filter).with_policy(policy)
     79     }
     80 
     81     #[cfg(feature = "nostr-client")]
     82     pub fn by_author(mut self, author: RadrootsNostrPublicKey) -> Self {
     83         self.filter = self.filter.author(author);
     84         self
     85     }
     86 
     87     pub fn named(mut self, name: impl Into<String>) -> Self {
     88         self.name = Some(name.into());
     89         self
     90     }
     91 
     92     pub fn stream_timeout_secs(mut self, value: u64) -> Self {
     93         self.stream_timeout_secs = value;
     94         self
     95     }
     96 
     97     pub fn reconnect_delay_millis(mut self, value: u64) -> Self {
     98         self.reconnect_delay_millis = value;
     99         self
    100     }
    101 }
    102 
    103 #[cfg(test)]
    104 mod tests {
    105     use super::*;
    106     #[cfg(feature = "nostr-client")]
    107     use radroots_nostr::prelude::RadrootsNostrKeys;
    108 
    109     fn base_spec() -> RadrootsNostrSubscriptionSpec {
    110         RadrootsNostrSubscriptionSpec {
    111             name: None,
    112             #[cfg(feature = "nostr-client")]
    113             filter: radroots_nostr::prelude::RadrootsNostrFilter::new(),
    114             policy: RadrootsNostrSubscriptionPolicy::Streaming,
    115             stream_timeout_secs: RadrootsNostrSubscriptionSpec::DEFAULT_STREAM_TIMEOUT_SECS,
    116             reconnect_delay_millis: RadrootsNostrSubscriptionSpec::DEFAULT_RECONNECT_DELAY_MILLIS,
    117         }
    118     }
    119 
    120     #[cfg(feature = "nostr-client")]
    121     #[test]
    122     fn text_notes_constructor_sets_defaults() {
    123         let spec = RadrootsNostrSubscriptionSpec::text_notes(
    124             Some(5),
    125             Some(10),
    126             RadrootsNostrSubscriptionPolicy::Streaming,
    127         );
    128         assert!(matches!(
    129             spec.policy,
    130             RadrootsNostrSubscriptionPolicy::Streaming
    131         ));
    132         assert_eq!(
    133             spec.stream_timeout_secs,
    134             RadrootsNostrSubscriptionSpec::DEFAULT_STREAM_TIMEOUT_SECS
    135         );
    136         assert_eq!(
    137             spec.reconnect_delay_millis,
    138             RadrootsNostrSubscriptionSpec::DEFAULT_RECONNECT_DELAY_MILLIS
    139         );
    140     }
    141 
    142     #[cfg(feature = "nostr-client")]
    143     #[test]
    144     fn by_kind_constructor_respects_policy() {
    145         let spec = RadrootsNostrSubscriptionSpec::by_kind(
    146             30023,
    147             None,
    148             None,
    149             RadrootsNostrSubscriptionPolicy::OneShotOnEose,
    150         );
    151         assert!(matches!(
    152             spec.policy,
    153             RadrootsNostrSubscriptionPolicy::OneShotOnEose
    154         ));
    155     }
    156 
    157     #[cfg(feature = "nostr-client")]
    158     #[test]
    159     fn builder_methods_update_spec_fields() {
    160         let keys = RadrootsNostrKeys::generate();
    161         let author = keys.public_key();
    162         let spec = RadrootsNostrSubscriptionSpec::text_notes(
    163             None,
    164             None,
    165             RadrootsNostrSubscriptionPolicy::Streaming,
    166         )
    167         .by_author(author)
    168         .named("posts")
    169         .stream_timeout_secs(12)
    170         .reconnect_delay_millis(99)
    171         .with_policy(RadrootsNostrSubscriptionPolicy::OneShotOnEose);
    172 
    173         assert_eq!(spec.name.as_deref(), Some("posts"));
    174         assert_eq!(spec.stream_timeout_secs, 12);
    175         assert_eq!(spec.reconnect_delay_millis, 99);
    176         assert_eq!(spec.policy, RadrootsNostrSubscriptionPolicy::OneShotOnEose);
    177     }
    178 
    179     #[test]
    180     fn builder_methods_update_common_fields_without_client_feature() {
    181         let spec = base_spec()
    182             .named("posts")
    183             .stream_timeout_secs(12)
    184             .reconnect_delay_millis(99)
    185             .with_policy(RadrootsNostrSubscriptionPolicy::OneShotOnEose);
    186 
    187         assert_eq!(spec.name.as_deref(), Some("posts"));
    188         assert_eq!(spec.stream_timeout_secs, 12);
    189         assert_eq!(spec.reconnect_delay_millis, 99);
    190         assert_eq!(spec.policy, RadrootsNostrSubscriptionPolicy::OneShotOnEose);
    191     }
    192 
    193     #[test]
    194     fn connection_snapshot_default_is_red() {
    195         let snapshot = RadrootsNostrConnectionSnapshot::default();
    196         assert_eq!(snapshot.light, RadrootsNostrTrafficLight::Red);
    197         assert_eq!(snapshot.connected, 0);
    198         assert_eq!(snapshot.connecting, 0);
    199         assert!(snapshot.last_error.is_none());
    200     }
    201 
    202     #[test]
    203     fn branch_probe_covers_true_and_false_paths() {
    204         let mut total = 0;
    205         for flag in [true, false] {
    206             if flag {
    207                 total += 1;
    208             } else {
    209                 total += 2;
    210             }
    211         }
    212         assert_eq!(total, 3);
    213     }
    214 }
    215 
    216 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
    217 pub struct RadrootsNostrSubscriptionHandle {
    218     pub id: String,
    219     pub name: Option<String>,
    220 }
    221 
    222 #[derive(Debug, Clone)]
    223 pub enum RadrootsNostrRuntimeEvent {
    224     RuntimeStarted,
    225     RuntimeStopped,
    226     SubscriptionOpened {
    227         id: String,
    228     },
    229     SubscriptionClosed {
    230         id: String,
    231     },
    232     Note {
    233         subscription_id: String,
    234         id: String,
    235         author: String,
    236         kind: u16,
    237         relay: Option<String>,
    238     },
    239     Notice {
    240         relay: String,
    241         message: String,
    242     },
    243     Error {
    244         message: String,
    245     },
    246 }
    247 
    248 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    249 pub enum RadrootsNostrTrafficLight {
    250     Red,
    251     Yellow,
    252     Green,
    253 }
    254 
    255 #[derive(Debug, Clone)]
    256 pub struct RadrootsNostrConnectionSnapshot {
    257     pub light: RadrootsNostrTrafficLight,
    258     pub connected: usize,
    259     pub connecting: usize,
    260     pub last_error: Option<String>,
    261 }
    262 
    263 impl Default for RadrootsNostrConnectionSnapshot {
    264     fn default() -> Self {
    265         Self {
    266             light: RadrootsNostrTrafficLight::Red,
    267             connected: 0,
    268             connecting: 0,
    269             last_error: None,
    270         }
    271     }
    272 }