use super::{Chat, MessageEntity, MessageKind, User};
use serde::{Deserialize, Deserializer};
use std::cmp::Reverse;
#[derive(Debug)]
pub struct Message {
pub id: u32,
pub date: i64,
pub chat: Chat,
pub from: Option<User>,
pub kind: MessageKind,
}
impl<'de> Deserialize<'de> for Message {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
struct Data {
#[serde(rename = "message_id")]
id: u32,
date: i64,
chat: Chat,
from: Option<User>,
#[serde(flatten)]
kind: MessageKind,
}
let mut data = Data::deserialize(de)?;
#[allow(clippy::single_match)]
match &mut data.kind {
MessageKind::Text { text, entities } => {
update_message_entity_index_to_utf8(text, entities)
}
_ => (),
}
Ok(Self {
id: data.id,
date: data.date,
chat: data.chat,
from: data.from,
kind: data.kind,
})
}
}
fn update_message_entity_index_to_utf8(text: &str, entities: &mut [MessageEntity]) {
for entity in entities.iter_mut() {
entity.length += entity.offset; }
let mut offsets = entities
.iter_mut()
.flat_map(|entity| [&mut entity.offset, &mut entity.length])
.collect::<Vec<_>>();
offsets.sort_unstable_by_key(|offset| Reverse(**offset));
let _ = text
.chars()
.chain(['\0']) .try_fold((0, 0), |(len_utf8, len_utf16), c| {
if offsets.is_empty() {
return None;
}
while offsets
.last()
.map(|offset| **offset <= len_utf16)
.unwrap_or(false)
{
let offset = offsets.pop().unwrap();
assert_eq!(*offset, len_utf16, "invalid utf-16 offset");
*offset = len_utf8;
}
Some((
len_utf8 + c.len_utf8() as u32,
len_utf16 + c.len_utf16() as u32,
))
});
for entity in entities.iter_mut() {
entity.length -= entity.offset; }
}