1use umsh_core::options::{OptionDecoder, OptionEncoder};
8
9use crate::model::{
10 ExtensionOptions, FRAGMENT_COUNT_MAX, Fragment, MessageSequence, MessageType, Regarding,
11 TextMessage, option,
12};
13use crate::{EncodeError, ParseError};
14
15#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
18pub struct ParseInfo {
19 pub seen_mask: u16,
21 pub repeated_presentation_mask: u16,
24}
25
26impl ParseInfo {
27 pub fn saw(&self, number: u16) -> bool {
28 number < 16 && self.seen_mask & (1 << number) != 0
29 }
30}
31
32fn parse_utf8(input: &[u8]) -> Result<&str, ParseError> {
33 core::str::from_utf8(input).map_err(|_| ParseError::InvalidUtf8)
34}
35
36pub fn parse(payload: &[u8]) -> Result<TextMessage<'_>, ParseError> {
38 parse_with_info(payload).map(|(message, _)| message)
39}
40
41pub fn parse_with_info(payload: &[u8]) -> Result<(TextMessage<'_>, ParseInfo), ParseError> {
44 let mut decoder = OptionDecoder::new(payload);
45 let mut message = TextMessage::basic("");
46 let mut info = ParseInfo::default();
47 let mut ext_range: Option<(usize, u16, usize)> = None; loop {
50 let snapshot_pos = decoder.position();
51 let snapshot_number = decoder.last_number();
52 let Some(item) = decoder.next() else {
53 if let Some((_, _, end)) = ext_range.as_mut() {
54 *end = snapshot_pos;
55 }
56 break;
57 };
58 let (number, value) = item?;
59
60 if number >= option::EXTENSION_BASE {
61 if ext_range.is_none() {
62 ext_range = Some((snapshot_pos, snapshot_number, snapshot_pos));
63 }
64 continue;
65 }
66
67 let bit = 1u16 << number;
68 if info.seen_mask & bit != 0 {
69 match number {
70 option::MESSAGE_TYPE
73 | option::MESSAGE_SEQUENCE
74 | option::REGARDING
75 | option::EDITING => return Err(ParseError::DuplicateOption(number)),
76 option::SEQUENCE_RESET | option::CHANNEL_GROUP_RESEND => {
79 if !value.is_empty() {
80 return Err(ParseError::InvalidOptionValue);
81 }
82 continue;
83 }
84 _ => {
86 info.repeated_presentation_mask |= bit;
87 continue;
88 }
89 }
90 }
91 info.seen_mask |= bit;
92
93 match number {
94 option::MESSAGE_TYPE => {
95 message.message_type = if value.is_empty() {
96 MessageType::Basic
97 } else if value.len() == 1 {
98 MessageType::from_byte(value[0])
99 } else {
100 return Err(ParseError::InvalidOptionValue);
101 };
102 }
103 option::SENDER_HANDLE => message.sender_handle = Some(parse_utf8(value)?),
104 option::MESSAGE_SEQUENCE => {
105 message.sequence = Some(match value {
106 [message_id] => MessageSequence {
107 message_id: *message_id,
108 fragment: None,
109 },
110 [message_id, index, count] if *count >= 2 && *index < *count => {
111 MessageSequence {
112 message_id: *message_id,
113 fragment: Some(Fragment {
114 index: *index,
115 count: *count,
116 }),
117 }
118 }
119 _ => return Err(ParseError::InvalidOptionValue),
120 });
121 }
122 option::SEQUENCE_RESET => {
123 if !value.is_empty() {
124 return Err(ParseError::InvalidOptionValue);
125 }
126 message.sequence_reset = true;
127 }
128 option::REGARDING => {
129 message.regarding = Some(match value {
130 [message_id] => Regarding::Unicast {
131 message_id: *message_id,
132 },
133 [message_id, a, b, c] => Regarding::Multicast {
134 message_id: *message_id,
135 source_prefix: umsh_core::NodeHint([*a, *b, *c]),
136 },
137 _ => return Err(ParseError::InvalidOptionValue),
138 });
139 }
140 option::EDITING => {
141 message.editing = match value {
142 [message_id] => Some(*message_id),
143 _ => return Err(ParseError::InvalidOptionValue),
144 };
145 }
146 option::BACKGROUND_COLOR => {
147 message.bg_color = match value {
148 [r, g, b] => Some([*r, *g, *b]),
149 _ => return Err(ParseError::InvalidOptionValue),
150 };
151 }
152 option::TEXT_COLOR => {
153 message.text_color = match value {
154 [r, g, b] => Some([*r, *g, *b]),
155 _ => return Err(ParseError::InvalidOptionValue),
156 };
157 }
158 option::CHANNEL_GROUP_RESEND => {
159 if !value.is_empty() {
160 return Err(ParseError::InvalidOptionValue);
161 }
162 message.channel_group_resend = true;
163 }
164 _ => unreachable!(),
165 }
166 }
167
168 if let Some((start, base_number, end)) = ext_range {
169 message.extensions = ExtensionOptions {
170 base_number,
171 data: &payload[start..end],
172 };
173 }
174 message.body = decoder.remainder();
175 Ok((message, info))
176}
177
178pub fn encode(msg: &TextMessage<'_>, buf: &mut [u8]) -> Result<usize, EncodeError> {
181 let mut encoder = OptionEncoder::new(buf);
182
183 if msg.message_type != MessageType::Basic {
184 encoder.put(option::MESSAGE_TYPE, &[msg.message_type.to_byte()])?;
185 }
186 if let Some(handle) = msg.sender_handle {
187 encoder.put(option::SENDER_HANDLE, handle.as_bytes())?;
188 }
189 if let Some(sequence) = msg.sequence {
190 let mut seq_buf = [0u8; 3];
191 let seq_len = if let Some(fragment) = sequence.fragment {
192 if fragment.count < 2
193 || fragment.index >= fragment.count
194 || fragment.count > FRAGMENT_COUNT_MAX
195 {
196 return Err(EncodeError::InvalidField);
197 }
198 seq_buf = [sequence.message_id, fragment.index, fragment.count];
199 3
200 } else {
201 seq_buf[0] = sequence.message_id;
202 1
203 };
204 encoder.put(option::MESSAGE_SEQUENCE, &seq_buf[..seq_len])?;
205 }
206 if msg.sequence_reset {
207 encoder.put(option::SEQUENCE_RESET, &[])?;
208 }
209 if let Some(regarding) = msg.regarding {
210 let mut regarding_buf = [0u8; 4];
211 let regarding_len = match regarding {
212 Regarding::Unicast { message_id } => {
213 regarding_buf[0] = message_id;
214 1
215 }
216 Regarding::Multicast {
217 message_id,
218 source_prefix,
219 } => {
220 regarding_buf = [
221 message_id,
222 source_prefix.0[0],
223 source_prefix.0[1],
224 source_prefix.0[2],
225 ];
226 4
227 }
228 };
229 encoder.put(option::REGARDING, ®arding_buf[..regarding_len])?;
230 }
231 if let Some(editing) = msg.editing {
232 encoder.put(option::EDITING, &[editing])?;
233 }
234 if let Some(color) = msg.bg_color {
235 encoder.put(option::BACKGROUND_COLOR, &color)?;
236 }
237 if let Some(color) = msg.text_color {
238 encoder.put(option::TEXT_COLOR, &color)?;
239 }
240 if msg.channel_group_resend {
241 encoder.put(option::CHANNEL_GROUP_RESEND, &[])?;
242 }
243 let mut last_ext = option::CHANNEL_GROUP_RESEND;
244 for item in msg.extensions.iter() {
245 let (number, value) = item.map_err(|_| EncodeError::InvalidField)?;
246 if number < option::EXTENSION_BASE || number < last_ext {
247 return Err(EncodeError::InvalidField);
248 }
249 last_ext = number;
250 encoder.put(number, value)?;
251 }
252
253 if !msg.body.is_empty() {
254 encoder.end_marker()?;
255 }
256 let prefix_len = encoder.finish();
257 if buf.len().saturating_sub(prefix_len) < msg.body.len() {
258 return Err(EncodeError::BufferTooSmall);
259 }
260 buf[prefix_len..prefix_len + msg.body.len()].copy_from_slice(msg.body);
261 Ok(prefix_len + msg.body.len())
262}