use mzrs_proto::prost::Message;
use serde::Deserialize;
#[derive(Debug, Clone, Deserialize, Default)]
pub struct ParsedMention {
pub user_id: Option<String>,
pub username: Option<String>,
pub role_id: Option<String>,
pub rolename: Option<String>,
pub s: Option<i32>,
pub e: Option<i32>,
}
#[derive(Debug, Clone, Deserialize, Default)]
pub struct ParsedAttachment {
pub filename: Option<String>,
pub filetype: Option<String>,
pub url: Option<String>,
pub size: Option<i64>,
pub width: Option<i32>,
pub height: Option<i32>,
pub duration: Option<i32>,
}
#[derive(Debug, Clone, Deserialize, Default)]
pub struct ParsedReference {
pub message_id: Option<String>,
pub message_ref_id: Option<String>,
pub content: Option<String>,
pub has_attachment: Option<bool>,
pub ref_type: Option<i32>,
pub message_sender_id: Option<String>,
pub message_sender_username: Option<String>,
pub message_sender_avatar: Option<String>,
pub message_sender_display_name: Option<String>,
}
fn opt_str(s: String) -> Option<String> {
if s.is_empty() {
None
} else {
Some(s)
}
}
fn opt_i64(v: i64) -> Option<String> {
if v == 0 {
None
} else {
Some(v.to_string())
}
}
fn opt_i32(v: i32) -> Option<i32> {
if v == 0 {
None
} else {
Some(v)
}
}
pub fn parse_text(content: &str) -> String {
#[derive(Deserialize, Default)]
struct C {
#[serde(default)]
t: String,
}
serde_json::from_str::<C>(content)
.map(|c| c.t)
.unwrap_or_default()
}
pub fn parse_mentions(bytes: &[u8]) -> Vec<ParsedMention> {
if bytes.is_empty() {
return vec![];
}
if bytes.first() == Some(&b'[') {
if let Ok(v) = serde_json::from_slice::<Vec<ParsedMention>>(bytes) {
return v;
}
}
mzrs_proto::api::MessageMentionList::decode(bytes)
.map(|list| {
list.mentions
.into_iter()
.map(|m| ParsedMention {
user_id: opt_i64(m.user_id),
username: opt_str(m.username),
role_id: opt_i64(m.role_id),
rolename: opt_str(m.rolename),
s: opt_i32(m.s),
e: opt_i32(m.e),
})
.collect()
})
.unwrap_or_default()
}
pub fn parse_attachments(bytes: &[u8]) -> Vec<ParsedAttachment> {
if bytes.is_empty() {
return vec![];
}
if bytes.first() == Some(&b'[') {
if let Ok(v) = serde_json::from_slice::<Vec<ParsedAttachment>>(bytes) {
return v;
}
}
mzrs_proto::api::MessageAttachmentList::decode(bytes)
.map(|list| {
list.attachments
.into_iter()
.map(|a| ParsedAttachment {
filename: opt_str(a.filename),
filetype: opt_str(a.filetype),
url: opt_str(a.url),
size: if a.size != 0 {
Some(a.size as i64)
} else {
None
},
width: if a.width != 0 { Some(a.width) } else { None },
height: if a.height != 0 { Some(a.height) } else { None },
duration: if a.duration != 0 {
Some(a.duration)
} else {
None
},
})
.collect()
})
.unwrap_or_default()
}
pub fn parse_references(bytes: &[u8]) -> Vec<ParsedReference> {
if bytes.is_empty() {
return vec![];
}
if bytes.first() == Some(&b'[') {
if let Ok(v) = serde_json::from_slice::<Vec<ParsedReference>>(bytes) {
return v;
}
}
mzrs_proto::api::MessageRefList::decode(bytes)
.map(|list| {
list.refs
.into_iter()
.map(|r| ParsedReference {
message_id: opt_i64(r.message_id),
message_ref_id: opt_i64(r.message_ref_id),
content: opt_str(r.content),
has_attachment: if r.has_attachment { Some(true) } else { None },
ref_type: opt_i32(r.ref_type),
message_sender_id: opt_i64(r.message_sender_id),
message_sender_username: opt_str(r.message_sender_username),
message_sender_avatar: opt_str(r.mesages_sender_avatar),
message_sender_display_name: opt_str(r.message_sender_display_name),
})
.collect()
})
.unwrap_or_default()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_text_extracts_t_field() {
assert_eq!(parse_text(r#"{"t":"hello"}"#), "hello");
}
#[test]
fn parse_text_empty_on_invalid() {
assert_eq!(parse_text("not json"), "");
}
#[test]
fn parse_text_empty_on_missing_t() {
assert_eq!(parse_text(r#"{"mk":[]}"#), "");
}
#[test]
fn parse_mentions_empty_bytes() {
assert!(parse_mentions(&[]).is_empty());
}
#[test]
fn parse_mentions_json_fallback() {
let json = r#"[{"user_id":"42","username":"alice","s":0,"e":6}]"#;
let m = parse_mentions(json.as_bytes());
assert_eq!(m.len(), 1);
assert_eq!(m[0].user_id.as_deref(), Some("42"));
}
#[test]
fn parse_attachments_empty_bytes() {
assert!(parse_attachments(&[]).is_empty());
}
#[test]
fn parse_references_empty_bytes() {
assert!(parse_references(&[]).is_empty());
}
}