syncweb_core/gossip/
topic_channel.rs1use std::marker::PhantomData;
2use std::sync::Arc;
3
4use bytes::Bytes;
5use iroh_gossip::{
6 TopicId,
7 api::{ApiError, Event, GossipSender},
8 net::Gossip,
9};
10use n0_future::{Stream, StreamExt};
11
12use crate::error::{Result, SyncwebError};
13use crate::gossip::signed_topic::SignedGossipMessage;
14
15pub struct TopicChannel<T: SignedGossipMessage + Send + Sync> {
22 gossip: Arc<Gossip>,
23 topic_id: TopicId,
24 sender: GossipSender,
25 _phantom: PhantomData<T>,
26}
27
28impl<T: SignedGossipMessage + Send + Sync> TopicChannel<T> {
29 #[must_use]
32 pub fn new(gossip: Arc<Gossip>, topic_name: &[u8], sender: GossipSender) -> Self {
33 Self {
34 gossip,
35 topic_id: TopicId::from_bytes(*blake3::hash(topic_name).as_bytes()),
36 sender,
37 _phantom: PhantomData,
38 }
39 }
40
41 pub async fn publish(&self, message: &T) -> Result<()> {
47 message.verify_signature()?;
48 let wire_bytes = message.to_wire_bytes()?;
49 let bytes = Bytes::from(wire_bytes);
50 self.sender
51 .broadcast(bytes)
52 .await
53 .map_err(|error| SyncwebError::operation("failed to publish gossip message", error))?;
54 Ok(())
55 }
56
57 pub fn receive_from(
67 &self,
68 stream: impl Stream<Item = std::result::Result<Event, ApiError>> + Send + 'static,
69 ) -> impl Stream<Item = T> + Send {
70 stream.filter_map(|event_result| match event_result {
71 Ok(Event::Received(msg)) => {
72 let parsed = match T::from_wire_bytes(&msg.content) {
73 Ok(m) => m,
74 Err(error) => {
75 tracing::debug!("failed to deserialize gossip message: {error}");
76 return None;
77 }
78 };
79 if let Err(error) = parsed.verify_signature() {
80 tracing::debug!("gossip message signature verification failed: {error}");
81 return None;
82 }
83 Some(parsed)
84 }
85 Ok(_) => None,
86 Err(error) => {
87 tracing::debug!("gossip stream error: {error}");
88 None
89 }
90 })
91 }
92
93 pub async fn collect_for(
102 &self,
103 stream: impl Stream<Item = std::result::Result<Event, ApiError>> + Send + 'static,
104 filter: impl Fn(&T) -> bool + Send + Sync + 'static,
105 timeout_duration: std::time::Duration,
106 ) -> Result<Vec<T>> {
107 let mut filtered = Box::pin(self.receive_from(stream));
108 let mut results = Vec::new();
109 let _ = tokio::time::timeout(timeout_duration, async {
110 while let Some(msg) = filtered.next().await {
111 if filter(&msg) {
112 results.push(msg);
113 }
114 }
115 })
116 .await;
117 Ok(results)
118 }
119
120 #[must_use]
121 pub const fn topic_id(&self) -> TopicId {
122 self.topic_id
123 }
124
125 #[must_use]
126 pub fn gossip(&self) -> &Gossip {
127 &self.gossip
128 }
129}