1use umsh_core::{ChannelTag, NodeHint, PublicKey};
4
5use crate::ParseError;
6
7pub mod option {
9 pub const MESSAGE_TYPE: u16 = 0;
10 pub const SENDER_HANDLE: u16 = 1;
11 pub const MESSAGE_SEQUENCE: u16 = 2;
12 pub const SEQUENCE_RESET: u16 = 3;
13 pub const REGARDING: u16 = 4;
14 pub const EDITING: u16 = 5;
15 pub const BACKGROUND_COLOR: u16 = 6;
16 pub const TEXT_COLOR: u16 = 7;
17 pub const CHANNEL_GROUP_RESEND: u16 = 8;
18
19 pub const EXTENSION_BASE: u16 = 9;
25}
26
27pub const FRAGMENT_BODY_MAX: usize = 160;
29
30pub const FRAGMENT_COUNT_MAX: u8 = 10;
32
33pub const REASSEMBLED_BODY_MAX: usize = FRAGMENT_BODY_MAX * FRAGMENT_COUNT_MAX as usize;
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
42pub enum MessageType {
43 Basic,
44 Status,
45 ResendRequest,
46 MessageUnavailable,
47 Extension(u8),
50}
51
52impl MessageType {
53 pub fn from_byte(value: u8) -> Self {
54 match value {
55 0 => Self::Basic,
56 1 => Self::Status,
57 2 => Self::ResendRequest,
58 3 => Self::MessageUnavailable,
59 other => Self::Extension(other),
60 }
61 }
62
63 pub fn to_byte(self) -> u8 {
64 match self {
65 Self::Basic => 0,
66 Self::Status => 1,
67 Self::ResendRequest => 2,
68 Self::MessageUnavailable => 3,
69 Self::Extension(value) => value,
70 }
71 }
72
73 pub fn is_control(self) -> bool {
75 matches!(self, Self::ResendRequest | Self::MessageUnavailable)
76 }
77}
78
79#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
81pub struct Fragment {
82 pub index: u8,
83 pub count: u8,
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
88pub struct MessageSequence {
89 pub message_id: u8,
90 pub fragment: Option<Fragment>,
91}
92
93impl MessageSequence {
94 pub fn unfragmented(message_id: u8) -> Self {
95 Self {
96 message_id,
97 fragment: None,
98 }
99 }
100}
101
102#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104pub enum Regarding {
105 Unicast { message_id: u8 },
107 Multicast {
109 message_id: u8,
110 source_prefix: NodeHint,
111 },
112}
113
114impl Regarding {
115 pub fn message_id(&self) -> u8 {
116 match self {
117 Self::Unicast { message_id } => *message_id,
118 Self::Multicast { message_id, .. } => *message_id,
119 }
120 }
121}
122
123#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
130pub struct ExtensionOptions<'a> {
131 pub(crate) base_number: u16,
132 pub(crate) data: &'a [u8],
133}
134
135impl<'a> ExtensionOptions<'a> {
136 pub const fn empty() -> Self {
138 Self {
139 base_number: 0,
140 data: &[],
141 }
142 }
143
144 pub fn is_empty(&self) -> bool {
145 self.data.is_empty()
146 }
147
148 pub fn iter(&self) -> ExtensionOptionsIter<'a> {
150 ExtensionOptionsIter {
151 decoder: umsh_core::options::OptionDecoder::new(self.data),
152 base_number: self.base_number,
153 }
154 }
155}
156
157pub struct ExtensionOptionsIter<'a> {
158 decoder: umsh_core::options::OptionDecoder<'a>,
159 base_number: u16,
160}
161
162impl<'a> Iterator for ExtensionOptionsIter<'a> {
163 type Item = Result<(u16, &'a [u8]), ParseError>;
164
165 fn next(&mut self) -> Option<Self::Item> {
166 let item = self.decoder.next()?;
167 Some(
168 item.map(|(delta_number, value)| (delta_number + self.base_number, value))
169 .map_err(ParseError::from),
170 )
171 }
172}
173
174#[derive(Clone, Copy, Debug, PartialEq, Eq)]
176pub struct TextMessage<'a> {
177 pub message_type: MessageType,
178 pub sender_handle: Option<&'a str>,
179 pub sequence: Option<MessageSequence>,
180 pub sequence_reset: bool,
181 pub regarding: Option<Regarding>,
182 pub editing: Option<u8>,
183 pub bg_color: Option<[u8; 3]>,
184 pub text_color: Option<[u8; 3]>,
185 pub channel_group_resend: bool,
186 pub extensions: ExtensionOptions<'a>,
189 pub body: &'a [u8],
196}
197
198impl<'a> TextMessage<'a> {
199 pub fn basic(body: &'a str) -> Self {
201 Self {
202 message_type: MessageType::Basic,
203 sender_handle: None,
204 sequence: None,
205 sequence_reset: false,
206 regarding: None,
207 editing: None,
208 bg_color: None,
209 text_color: None,
210 channel_group_resend: false,
211 extensions: ExtensionOptions::empty(),
212 body: body.as_bytes(),
213 }
214 }
215
216 pub fn body_str(&self) -> Result<&'a str, ParseError> {
218 core::str::from_utf8(self.body).map_err(|_| ParseError::InvalidUtf8)
219 }
220}
221
222#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
224pub enum ConversationKey {
225 Direct { peer: PublicKey },
227 ChannelGroup { channel: ChannelTag },
234 ChannelDirect {
236 channel: ChannelTag,
237 peer: PublicKey,
238 },
239 Room { room: PublicKey },
241}
242
243impl ConversationKey {
244 pub fn uses_multicast_references(&self) -> bool {
246 matches!(self, Self::ChannelGroup { .. })
247 }
248}
249
250#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
253pub enum SenderScope {
254 Local,
256 Peer(PublicKey),
258 ClaimedMember(NodeHint),
261}
262
263impl SenderScope {
264 pub fn hint(&self) -> Option<NodeHint> {
266 match self {
267 Self::Local => None,
268 Self::Peer(key) => Some(NodeHint([key.0[0], key.0[1], key.0[2]])),
269 Self::ClaimedMember(hint) => Some(*hint),
270 }
271 }
272}
273
274#[derive(Clone, Copy, Debug, PartialEq, Eq)]
276pub enum WireRef {
277 SenderScoped { sender: SenderScope, message_id: u8 },
279 RoomCanonical { message_id: u8 },
281}