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