1use alloc::vec::Vec;
13
14mod inbound;
15mod transmit;
16
17pub use inbound::{Inbound, InboundText, Refusal, RxSummary, WellKnownChannels, inbound_text};
18#[cfg(feature = "software-crypto")]
19pub use transmit::bound_channel;
20pub use transmit::{
21 Departed, Departure, Sent, Stall, TRANSMISSION_STALL_MS, TransmitPolicy, TransmitQueue,
22 Undeliverable, WELL_KNOWN_CHANNEL_MAX_FLOOD_HOPS, channel_reach, transmit, with_flood_reach,
23};
24
25use umsh_core::{PacketType, PayloadType};
26use umsh_mac::SendOptions;
27use umsh_node::{LocalNode, PeerConnection, SendProgressTicket, Subscription, Transport};
28
29#[cfg(feature = "software-crypto")]
30use umsh_node::{BoundChannel, MacBackend};
31
32use crate::{
33 EncodeError, OwnedTextMessage, ParseError, TextMessage, TextSendError, encode_text_message,
34 parse_text_message,
35};
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub enum TextReceiveIssue {
40 WrongPayloadType(PayloadType),
41 Parse(ParseError),
42}
43
44pub fn parse_text_payload(
46 packet_type: PacketType,
47 payload: &[u8],
48) -> Result<TextMessage<'_>, ParseError> {
49 parse_text_message(expect_payload_type(
50 packet_type,
51 payload,
52 PayloadType::TextMessage,
53 )?)
54}
55
56#[derive(Clone)]
58pub struct UnicastTextChatWrapper<T: Transport + Clone> {
59 peer: PeerConnection<T>,
60}
61
62impl<T: Transport + Clone> UnicastTextChatWrapper<T> {
63 pub fn new(peer: PeerConnection<T>) -> Self {
64 Self { peer }
65 }
66
67 pub fn from_peer(peer: &PeerConnection<T>) -> Self {
68 Self { peer: peer.clone() }
69 }
70
71 pub fn peer_connection(&self) -> &PeerConnection<T> {
72 &self.peer
73 }
74
75 pub fn peer(&self) -> &umsh_core::PublicKey {
76 self.peer.peer()
77 }
78
79 pub async fn send_message(
80 &self,
81 message: &TextMessage<'_>,
82 options: &SendOptions,
83 ) -> Result<SendProgressTicket, TextSendError<T::Error>> {
84 let payload = encode_text_payload(message)?;
85 self.peer
86 .send(&payload, options)
87 .await
88 .map_err(TextSendError::Transport)
89 }
90
91 pub async fn send_owned_message(
92 &self,
93 message: &OwnedTextMessage,
94 options: &SendOptions,
95 ) -> Result<SendProgressTicket, TextSendError<T::Error>> {
96 self.send_message(&message.as_borrowed(), options).await
97 }
98
99 pub async fn send_text(
100 &self,
101 body: &str,
102 options: &SendOptions,
103 ) -> Result<SendProgressTicket, TextSendError<T::Error>> {
104 self.send_message(&TextMessage::basic(body), options).await
105 }
106}
107
108impl<M: umsh_node::MacBackend> UnicastTextChatWrapper<LocalNode<M>> {
109 pub fn on_text<F>(&self, handler: F) -> Subscription
110 where
111 F: FnMut(&umsh_node::ReceivedPacketRef<'_>, TextMessage<'_>) + 'static,
112 {
113 self.on_text_with_diagnostics(handler, |_, _| {})
114 }
115
116 pub fn on_text_with_diagnostics<F, D>(&self, mut handler: F, mut diagnostics: D) -> Subscription
117 where
118 F: FnMut(&umsh_node::ReceivedPacketRef<'_>, TextMessage<'_>) + 'static,
119 D: FnMut(&umsh_node::ReceivedPacketRef<'_>, TextReceiveIssue) + 'static,
120 {
121 self.peer.on_receive(move |packet| {
122 if packet.payload_type() != PayloadType::TextMessage {
123 diagnostics(
124 packet,
125 TextReceiveIssue::WrongPayloadType(packet.payload_type()),
126 );
127 return false;
128 }
129 let message = match parse_text_message(packet.payload()) {
130 Ok(message) => message,
131 Err(error) => {
132 diagnostics(packet, TextReceiveIssue::Parse(error));
133 return false;
134 }
135 };
136 handler(packet, message);
137 true
138 })
139 }
140}
141
142#[cfg(feature = "software-crypto")]
143#[derive(Clone)]
144pub struct MulticastTextChatWrapper<M: MacBackend> {
145 channel: BoundChannel<M>,
146}
147
148#[cfg(feature = "software-crypto")]
149impl<M: MacBackend> MulticastTextChatWrapper<M> {
150 pub fn new(channel: BoundChannel<M>) -> Self {
151 Self { channel }
152 }
153
154 pub fn from_channel(channel: &BoundChannel<M>) -> Self {
155 Self {
156 channel: channel.clone(),
157 }
158 }
159
160 pub fn bound_channel(&self) -> &BoundChannel<M> {
161 &self.channel
162 }
163
164 pub async fn send_message(
165 &self,
166 message: &TextMessage<'_>,
167 options: &SendOptions,
168 ) -> Result<SendProgressTicket, TextSendError<umsh_node::NodeError<M>>> {
169 let payload = encode_text_payload(message)?;
170 self.channel
171 .send_all(&payload, options)
172 .await
173 .map_err(TextSendError::Transport)
174 }
175
176 pub async fn send_owned_message(
177 &self,
178 message: &OwnedTextMessage,
179 options: &SendOptions,
180 ) -> Result<SendProgressTicket, TextSendError<umsh_node::NodeError<M>>> {
181 self.send_message(&message.as_borrowed(), options).await
182 }
183
184 pub async fn send_text(
185 &self,
186 body: &str,
187 options: &SendOptions,
188 ) -> Result<SendProgressTicket, TextSendError<umsh_node::NodeError<M>>> {
189 self.send_message(&TextMessage::basic(body), options).await
190 }
191
192 pub fn on_text<F>(&self, handler: F) -> Subscription
193 where
194 F: FnMut(&umsh_node::ReceivedPacketRef<'_>, TextMessage<'_>) + 'static,
195 {
196 self.on_text_with_diagnostics(handler, |_, _| {})
197 }
198
199 pub fn on_text_with_diagnostics<F, D>(&self, mut handler: F, mut diagnostics: D) -> Subscription
200 where
201 F: FnMut(&umsh_node::ReceivedPacketRef<'_>, TextMessage<'_>) + 'static,
202 D: FnMut(&umsh_node::ReceivedPacketRef<'_>, TextReceiveIssue) + 'static,
203 {
204 let channel_id = *self.channel.channel().channel_id();
205 self.channel
206 .node()
207 .on_receive(move |packet: &umsh_node::ReceivedPacketRef<'_>| {
208 let Some(channel) = packet.channel() else {
209 return false;
210 };
211 if channel.id() != channel_id {
212 return false;
213 }
214 if packet.payload_type() != PayloadType::TextMessage {
215 diagnostics(
216 packet,
217 TextReceiveIssue::WrongPayloadType(packet.payload_type()),
218 );
219 return false;
220 }
221 let message = match parse_text_message(packet.payload()) {
222 Ok(message) => message,
223 Err(error) => {
224 diagnostics(packet, TextReceiveIssue::Parse(error));
225 return false;
226 }
227 };
228 handler(packet, message);
229 true
230 })
231 }
232}
233
234fn encode_text_payload(message: &TextMessage<'_>) -> Result<Vec<u8>, EncodeError> {
235 let mut body = [0u8; 512];
236 let len = encode_text_message(message, &mut body)?;
237 let mut payload = Vec::with_capacity(len + 1);
238 payload.push(PayloadType::TextMessage as u8);
239 payload.extend_from_slice(&body[..len]);
240 Ok(payload)
241}
242
243fn split_payload_type(payload: &[u8]) -> Result<(PayloadType, &[u8]), ParseError> {
245 if payload.is_empty() {
246 return Ok((PayloadType::Empty, &[]));
247 }
248 if let Some(payload_type) = PayloadType::from_byte(payload[0]) {
249 Ok((payload_type, &payload[1..]))
250 } else {
251 Ok((PayloadType::Empty, payload))
252 }
253}
254
255fn expect_payload_type(
257 packet_type: PacketType,
258 payload: &[u8],
259 expected: PayloadType,
260) -> Result<&[u8], ParseError> {
261 let (payload_type, body) = split_payload_type(payload)?;
262 if !payload_type.allowed_for(packet_type) {
263 return Err(ParseError::PayloadTypeNotAllowed {
264 payload_type: payload_type as u8,
265 packet_type,
266 });
267 }
268 if payload_type != expected {
269 return Err(ParseError::InvalidPayloadType(payload_type as u8));
270 }
271 Ok(body)
272}