use crate::chat::{
events::{EventMeta, ParticipantListCompleteEvent},
parser::{
raw::{ParseResult, RawMessage},
user::parse_user_status,
util::normalize_user_id,
},
types::User,
};
#[derive(Debug, Clone)]
pub struct ParticipantListChunk {
pub entries: Vec<User>,
}
pub fn parse_participant_list_chunk(raw: RawMessage) -> ParseResult<Option<ParticipantListChunk>> {
let fields = body_without_trailing_empty(&raw.body);
if fields.len() < 4 || fields.first() != Some(&"1") || (fields.len() - 1) % 3 != 0 {
return Ok(None);
}
let mut entries = Vec::with_capacity((fields.len() - 1) / 3);
for chunk in fields[1..].chunks_exact(3) {
let id = normalize_user_id(chunk[0]);
let label = chunk[1].to_string();
let flags = chunk[2];
if !is_user_flags(flags) {
return Ok(None);
}
entries.push(User {
id,
label,
status: parse_user_status(flags),
subscribe: None,
});
}
Ok(Some(ParticipantListChunk { entries }))
}
pub fn parse_participant_list_complete_event(
raw: RawMessage,
) -> ParseResult<ParticipantListCompleteEvent> {
Ok(ParticipantListCompleteEvent {
meta: EventMeta {
received_time: raw.received_time,
},
result_code: raw.field(0)?.to_string(),
})
}
fn body_without_trailing_empty(body: &[String]) -> Vec<&str> {
let mut fields: Vec<&str> = body.iter().map(String::as_str).collect();
if fields.last() == Some(&"") {
fields.pop();
}
fields
}
fn is_user_flags(value: &str) -> bool {
let Some((combined, follow)) = value.split_once('|') else {
return false;
};
combined.parse::<u32>().is_ok() && follow.parse::<u32>().is_ok()
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use crate::chat::parser::raw::parse_message;
fn frame(code: u32, body: &[u8]) -> RawMessage {
let mut bytes = Vec::new();
bytes.extend_from_slice(&[27, 9]);
bytes.extend_from_slice(format!("{code:04}").as_bytes());
bytes.extend_from_slice(format!("{:06}", body.len()).as_bytes());
bytes.extend_from_slice(b"00");
bytes.extend_from_slice(body);
let bytes: Arc<[u8]> = bytes.into();
parse_message(bytes).unwrap()
}
#[test]
fn parses_participant_list_chunk() {
let raw = frame(
4,
b"\x0c1\x0cwoalsdl1234\x0c\xec\x9d\xb4\xed\x8c\x8c\xeb\xa6\xac\xec\x95\x84\x0c537477152|32768\x0cksch1028(3)\x0cuser\x0c268550176|557056\x0c",
);
let event = parse_participant_list_chunk(raw).unwrap().unwrap();
assert_eq!(event.entries.len(), 2);
assert_eq!(event.entries[0].id, "woalsdl1234");
assert_eq!(event.entries[0].label, "이파리아");
assert_eq!(event.entries[1].id, "ksch1028");
assert!(event.entries[0].subscribe.is_none());
}
#[test]
fn parses_participant_list_complete_frame() {
let bytes: Arc<[u8]> = b"\x1b\t011000000300\x0c1\x0c".to_vec().into();
let raw = parse_message(bytes).unwrap();
let event = parse_participant_list_complete_event(raw).unwrap();
assert_eq!(event.result_code, "1");
}
}