use crate::client::{Client, Message};
use crate::dispatch::EventSource;
use crate::events::Event;
use crate::types::{ChannelId, UserId, UserState, UserStatus};
use crate::utils::ToTT;
use std::collections::VecDeque;
use teamtalk_sys as ffi;
pub struct MockClient {
queue: VecDeque<(Event, Message)>,
}
impl MockClient {
pub fn new() -> Self {
Self {
queue: VecDeque::new(),
}
}
pub fn push(&mut self, event: Event, message: Message) -> &mut Self {
self.queue.push_back((event, message));
self
}
pub fn push_event(&mut self, event: Event) -> &mut Self {
self.push(event, MockMessage::empty())
}
pub fn push_user_joined(&mut self, user: MockUserBuilder) -> &mut Self {
self.push(Event::UserJoined, user.build_for(Event::UserJoined))
}
pub fn push_user_update(&mut self, user: MockUserBuilder) -> &mut Self {
self.push(Event::UserUpdate, user.build_for(Event::UserUpdate))
}
pub fn push_text_message(&mut self, message: Message) -> &mut Self {
self.push(Event::TextMessage, message)
}
pub fn len(&self) -> usize {
self.queue.len()
}
pub fn is_empty(&self) -> bool {
self.queue.is_empty()
}
}
impl Default for MockClient {
fn default() -> Self {
Self::new()
}
}
impl EventSource for MockClient {
fn poll(&mut self, _timeout_ms: i32) -> Option<(Event, Message)> {
self.queue.pop_front()
}
fn client(&self) -> Option<&Client> {
None
}
}
pub struct MockMessage;
impl MockMessage {
pub fn empty() -> Message {
Message::from_raw(Event::None, unsafe { std::mem::zeroed() })
}
pub fn text(
msg_type: ffi::TextMsgType,
from_id: UserId,
to_id: UserId,
channel_id: ChannelId,
from_username: &str,
text: &str,
) -> Message {
let mut msg = unsafe { std::mem::zeroed::<ffi::TextMessage>() };
msg.nMsgType = msg_type;
msg.nFromUserID = from_id.0;
msg.nToUserID = to_id.0;
msg.nChannelID = channel_id.0;
write_tt(&mut msg.szFromUsername, from_username);
write_tt(&mut msg.szMessage, text);
message_from_text(msg, from_id.0)
}
pub fn remote_file(event: Event, remote_file: ffi::RemoteFile) -> Message {
let mut msg = unsafe { std::mem::zeroed::<ffi::TTMessage>() };
msg.ttType = ffi::TTType::__REMOTEFILE;
msg.nSource = remote_file.nChannelID;
msg.__bindgen_anon_1.remotefile = remote_file;
Message::from_raw(event, msg)
}
pub fn banned_user(entry: ffi::BannedUser) -> Message {
let mut msg = unsafe { std::mem::zeroed::<ffi::TTMessage>() };
msg.ttType = ffi::TTType::__BANNEDUSER;
msg.__bindgen_anon_1.banneduser = entry;
Message::from_raw(Event::BannedUser, msg)
}
pub fn desktop_input(input: ffi::DesktopInput, source: i32) -> Message {
let mut msg = unsafe { std::mem::zeroed::<ffi::TTMessage>() };
msg.ttType = ffi::TTType::__DESKTOPINPUT;
msg.nSource = source;
msg.__bindgen_anon_1.desktopinput = input;
Message::from_raw(Event::DesktopInput, msg)
}
pub fn media_file_info(event: Event, info: ffi::MediaFileInfo) -> Message {
let mut msg = unsafe { std::mem::zeroed::<ffi::TTMessage>() };
msg.ttType = ffi::TTType::__MEDIAFILEINFO;
msg.__bindgen_anon_1.mediafileinfo = info;
Message::from_raw(event, msg)
}
pub fn audio_input_progress(progress: ffi::AudioInputProgress, source: i32) -> Message {
let mut msg = unsafe { std::mem::zeroed::<ffi::TTMessage>() };
msg.ttType = ffi::TTType::__AUDIOINPUTPROGRESS;
msg.nSource = source;
msg.__bindgen_anon_1.audioinputprogress = progress;
Message::from_raw(Event::AudioInput, msg)
}
}
pub struct MockUserBuilder {
user: ffi::User,
}
impl MockUserBuilder {
pub fn new(id: UserId) -> Self {
let mut user = unsafe { std::mem::zeroed::<ffi::User>() };
user.nUserID = id.0;
Self { user }
}
pub fn username(mut self, username: &str) -> Self {
write_tt(&mut self.user.szUsername, username);
self
}
pub fn nickname(mut self, nickname: &str) -> Self {
write_tt(&mut self.user.szNickname, nickname);
self
}
pub fn client_name(mut self, client_name: &str) -> Self {
write_tt(&mut self.user.szClientName, client_name);
self
}
pub fn ip_address(mut self, ip_address: &str) -> Self {
write_tt(&mut self.user.szIPAddress, ip_address);
self
}
pub fn channel_id(mut self, channel_id: ChannelId) -> Self {
self.user.nChannelID = channel_id.0;
self
}
pub fn status(mut self, status: UserStatus) -> Self {
self.user.nStatusMode = status.to_bits() as i32;
self
}
pub fn state(mut self, state: UserState) -> Self {
self.user.uUserState = state.raw();
self
}
pub fn user_data(mut self, user_data: i32) -> Self {
self.user.nUserData = user_data;
self
}
pub fn user_type(mut self, user_type: u32) -> Self {
self.user.uUserType = user_type;
self
}
pub fn version(mut self, version: u32) -> Self {
self.user.uVersion = version;
self
}
pub fn build_for(self, event: Event) -> Message {
message_from_user(self.user, event)
}
}
fn write_tt(dst: &mut [ffi::TTCHAR], value: &str) {
dst.fill(0);
let tt = value.tt();
let len = tt.len().min(dst.len());
dst[..len].copy_from_slice(&tt[..len]);
if len == dst.len() {
dst[dst.len() - 1] = 0;
}
}
fn message_from_user(user: ffi::User, event: Event) -> Message {
let mut msg = unsafe { std::mem::zeroed::<ffi::TTMessage>() };
msg.nSource = user.nUserID;
msg.ttType = ffi::TTType::__USER;
msg.__bindgen_anon_1.user = user;
Message::from_raw(event, msg)
}
fn message_from_text(text: ffi::TextMessage, source: i32) -> Message {
let mut msg = unsafe { std::mem::zeroed::<ffi::TTMessage>() };
msg.nSource = source;
msg.ttType = ffi::TTType::__TEXTMESSAGE;
msg.__bindgen_anon_1.textmessage = text;
Message::from_raw(Event::TextMessage, msg)
}