notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 5f3f1f01191e6c94234d7a6e0722f8ba327d9a17
parent 0e9665c9de7fdceadcbbdc4d9652bf7326876dcf
Author: kernelkind <kernelkind@gmail.com>
Date:   Thu,  5 Feb 2026 16:21:27 -0500

feat(outbox): add `OutboxSubscriptions`

Signed-off-by: kernelkind <kernelkind@gmail.com>

Diffstat:
Mcrates/enostr/src/relay/mod.rs | 5+++++
Acrates/enostr/src/relay/subscription.rs | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 154 insertions(+), 0 deletions(-)

diff --git a/crates/enostr/src/relay/mod.rs b/crates/enostr/src/relay/mod.rs @@ -6,6 +6,7 @@ mod multicast; pub mod pool; mod queue; pub mod subs_debug; +mod subscription; mod websocket; pub use broadcast::{BroadcastCache, BroadcastRelay}; @@ -18,6 +19,10 @@ pub use limits::{ pub use multicast::{MulticastRelay, MulticastRelayCache}; use nostrdb::Filter; pub use queue::QueuedTasks; +pub use subscription::{ + FullModificationTask, ModifyFiltersTask, ModifyRelaysTask, ModifyTask, OutboxSubscriptions, + OutboxTask, SubscribeTask, +}; pub use websocket::{WebsocketConn, WebsocketRelay}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/crates/enostr/src/relay/subscription.rs b/crates/enostr/src/relay/subscription.rs @@ -0,0 +1,149 @@ +use hashbrown::{HashMap, HashSet}; +use nostrdb::Filter; + +use crate::relay::{MetadataFilters, NormRelayUrl, OutboxSubId, RelayType, RelayUrlPkgs}; + +pub struct OutboxSubscription { + pub relays: HashSet<NormRelayUrl>, + pub filters: MetadataFilters, + json_size: usize, + pub is_oneshot: bool, + pub relay_type: RelayType, +} + +impl OutboxSubscription { + pub fn see_all(&mut self, at: u64) { + for (_, meta) in self.filters.iter_mut() { + meta.last_seen = Some(at); + } + } + + pub fn ingest_task(&mut self, task: ModifyTask) { + match task { + ModifyTask::Filters(modify_filters_task) => { + self.filters = MetadataFilters::new(modify_filters_task.0); + self.json_size = self.filters.json_size_sum(); + } + ModifyTask::Relays(modify_relays_task) => { + self.relays = modify_relays_task.0; + } + ModifyTask::Full(full_modification_task) => { + self.filters = MetadataFilters::new(full_modification_task.filters); + self.json_size = self.filters.json_size_sum(); + self.relays = full_modification_task.relays; + } + } + } +} + +#[derive(Default)] +pub struct OutboxSubscriptions { + subs: HashMap<OutboxSubId, OutboxSubscription>, +} + +impl OutboxSubscriptions { + pub fn view(&self, id: &OutboxSubId) -> Option<SubscriptionView<'_>> { + let sub = self.subs.get(id)?; + + Some(SubscriptionView { + id: *id, + filters: &sub.filters, + json_size: sub.json_size, + is_oneshot: sub.is_oneshot, + }) + } + + pub fn json_size(&self, id: &OutboxSubId) -> Option<usize> { + self.subs.get(id).map(|s| s.json_size) + } + + pub fn subset_oneshot(&self, ids: &HashSet<OutboxSubId>) -> HashSet<OutboxSubId> { + ids.iter() + .filter(|id| self.subs.get(*id).is_some_and(|s| s.is_oneshot)) + .copied() + .collect() + } + + pub fn is_oneshot(&self, id: &OutboxSubId) -> bool { + self.subs.get(id).is_some_and(|s| s.is_oneshot) + } + + pub fn json_size_sum(&self, ids: &HashSet<OutboxSubId>) -> usize { + ids.iter() + .map(|id| self.subs.get(id).map_or(0, |s| s.json_size)) + .sum() + } + + pub fn filters_all(&self, ids: &HashSet<OutboxSubId>) -> Vec<Filter> { + ids.iter() + .filter_map(|id| self.subs.get(id)) + .flat_map(|sub| sub.filters.filters.iter().cloned()) + .collect() + } + + pub fn get_mut(&mut self, id: &OutboxSubId) -> Option<&mut OutboxSubscription> { + self.subs.get_mut(id) + } + + pub fn get(&self, id: &OutboxSubId) -> Option<&OutboxSubscription> { + self.subs.get(id) + } + + pub fn remove(&mut self, id: &OutboxSubId) { + self.subs.remove(id); + } + + pub fn new_subscription(&mut self, id: OutboxSubId, task: SubscribeTask, is_oneshot: bool) { + let filters = MetadataFilters::new(task.filters); + let json_size = filters.json_size_sum(); + self.subs.insert( + id, + OutboxSubscription { + relays: task.relays.urls, + filters, + json_size, + is_oneshot, + relay_type: if task.relays.use_transparent { + RelayType::Transparent + } else { + RelayType::Compaction + }, + }, + ); + } +} + +pub struct SubscriptionView<'a> { + pub id: OutboxSubId, + pub filters: &'a MetadataFilters, + pub json_size: usize, + pub is_oneshot: bool, +} + +pub enum OutboxTask { + Modify(ModifyTask), + Subscribe(SubscribeTask), + Unsubscribe, + Oneshot(SubscribeTask), +} + +pub enum ModifyTask { + Filters(ModifyFiltersTask), + Relays(ModifyRelaysTask), + Full(FullModificationTask), +} + +#[derive(Default)] +pub struct ModifyFiltersTask(pub Vec<Filter>); + +pub struct ModifyRelaysTask(pub HashSet<NormRelayUrl>); + +pub struct FullModificationTask { + pub filters: Vec<Filter>, + pub relays: HashSet<NormRelayUrl>, +} + +pub struct SubscribeTask { + pub filters: Vec<Filter>, + pub relays: RelayUrlPkgs, +}