#![allow(dead_code)]
extern crate chrono;
#[macro_use]
extern crate lazy_static;
extern crate ts3plugin_sys;
pub use ts3plugin_sys::plugin_definitions::*;
pub use ts3plugin_sys::public_definitions::*;
pub use ts3plugin_sys::public_errors::Error;
pub use ts3plugin_sys::ts3functions::Ts3Functions;
pub use crate::plugin::*;
use chrono::*;
use std::collections::HashMap as Map;
use std::ffi::{CStr, CString};
use std::fmt;
use std::mem::transmute;
use std::ops::{Deref, DerefMut};
use std::os::raw::{c_char, c_int};
use std::sync::{MutexGuard, RwLock};
macro_rules! to_cstring {
($string: expr_2021) => {
CString::new($string).unwrap_or(CString::new("String contains null character").unwrap())
};
}
macro_rules! to_string {
($string: expr_2021) => {{ String::from_utf8_lossy(CStr::from_ptr($string).to_bytes()).into_owned() }};
}
pub mod plugin;
pub mod ts3interface;
include!(concat!(env!("OUT_DIR"), "/channel.rs"));
include!(concat!(env!("OUT_DIR"), "/connection.rs"));
include!(concat!(env!("OUT_DIR"), "/server.rs"));
#[doc(hidden)]
pub static TS3_FUNCTIONS: RwLock<Option<Ts3Functions>> = RwLock::new(None);
#[derive(Clone)]
pub enum MessageReceiver {
Connection(ConnectionId),
Channel,
Server,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Permissions;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct ServerId(u64);
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct ChannelId(u64);
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct ConnectionId(u16);
#[derive(Debug, Clone)]
pub struct Permission {}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct PermissionId(u32);
#[derive(Debug, Clone)]
pub struct ServerGroup {}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct ServerGroupId(u64);
#[derive(Debug, Clone)]
pub struct ChannelGroup {}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct ChannelGroupId(u64);
#[derive(Debug, Eq)]
pub struct InvokerData {
id: ConnectionId,
uid: String,
name: String,
}
impl PartialEq<InvokerData> for InvokerData {
fn eq(&self, other: &InvokerData) -> bool {
self.id == other.id
}
}
impl InvokerData {
fn new(id: ConnectionId, uid: String, name: String) -> InvokerData {
InvokerData { id, uid, name }
}
pub fn get_id(&self) -> ConnectionId {
self.id
}
pub fn get_uid(&self) -> &String {
&self.uid
}
pub fn get_name(&self) -> &String {
&self.name
}
}
#[derive(Debug, Eq)]
pub struct Invoker<'a> {
server: Server<'a>,
data: InvokerData,
}
impl<'a, 'b> PartialEq<Invoker<'b>> for Invoker<'a> {
fn eq(&self, other: &Invoker) -> bool {
self.server == other.server && self.data == other.data
}
}
impl<'a> Deref for Invoker<'a> {
type Target = InvokerData;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<'a> Invoker<'a> {
fn new(server: Server<'a>, data: InvokerData) -> Invoker<'a> {
Invoker { server, data }
}
pub fn get_connection(&'_ self) -> Option<Connection<'_>> {
self.server.get_connection(self.id)
}
}
#[derive(Clone)]
pub struct Server<'a> {
api: &'a TsApi,
data: Result<&'a ServerData, ServerId>,
}
impl<'a, 'b> PartialEq<Server<'b>> for Server<'a> {
fn eq(&self, other: &Server<'b>) -> bool {
self.get_id() == other.get_id()
}
}
impl<'a> Eq for Server<'a> {}
impl<'a> fmt::Debug for Server<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Server({})", self.get_id().0)
}
}
impl PartialEq<ServerData> for ServerData {
fn eq(&self, other: &ServerData) -> bool {
self.id == other.id
}
}
impl Eq for ServerData {}
impl ServerData {
fn get_property_as_string(
id: ServerId, property: VirtualServerProperties,
) -> Result<String, Error> {
unsafe {
let mut name: *mut c_char = std::ptr::null_mut();
let res: Error =
transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_server_variable_as_string)(id.0, property as usize, &mut name));
match res {
Error::Ok => Ok(to_string!(name)),
_ => Err(res),
}
}
}
fn get_property_as_int(id: ServerId, property: VirtualServerProperties) -> Result<i32, Error> {
unsafe {
let mut number: c_int = 0;
let res: Error =
transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_server_variable_as_int)(id.0, property as usize, &mut number));
match res {
Error::Ok => Ok(number as i32),
_ => Err(res),
}
}
}
fn get_property_as_uint64(
id: ServerId, property: VirtualServerProperties,
) -> Result<u64, Error> {
unsafe {
let mut number: u64 = 0;
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_server_variable_as_uint64)(
id.0, property as usize, &mut number
));
match res {
Error::Ok => Ok(number),
_ => Err(res),
}
}
}
fn query_own_connection_id(id: ServerId) -> Result<ConnectionId, Error> {
unsafe {
let mut number: u16 = 0;
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_client_id)(id.0, &mut number));
match res {
Error::Ok => Ok(ConnectionId(number)),
_ => Err(res),
}
}
}
fn query_connections(id: ServerId) -> Map<ConnectionId, ConnectionData> {
let mut map = Map::new();
let mut result: *mut u16 = std::ptr::null_mut();
let res: Error = unsafe {
transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_client_list)(id.0, &mut result))
};
if res == Error::Ok {
unsafe {
let mut counter = 0;
while *result.offset(counter) != 0 {
let connection_id = ConnectionId(*result.offset(counter));
let mut connection = ConnectionData::new(id, connection_id);
connection.update();
map.insert(connection_id, connection);
counter += 1;
}
}
}
map
}
fn query_channels(id: ServerId) -> Result<Map<ChannelId, ChannelData>, Error> {
let mut map = Map::new();
let mut result: *mut u64 = std::ptr::null_mut();
let res: Error = unsafe {
transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_channel_list)(id.0, &mut result))
};
if res == Error::Ok {
unsafe {
let mut counter = 0;
while *result.offset(counter) != 0 {
let channel_id = ChannelId(*result.offset(counter));
let mut channel = ChannelData::new(id, channel_id);
channel.update();
map.insert(channel_id, channel);
counter += 1;
}
}
Ok(map)
} else {
Err(res)
}
}
fn add_connection(&mut self, connection_id: ConnectionId) -> &mut ConnectionData {
let mut connection = ConnectionData::new(self.id, connection_id);
connection.update();
self.visible_connections.insert(connection_id, connection);
self.visible_connections.get_mut(&connection_id).unwrap()
}
fn remove_connection(&mut self, connection_id: ConnectionId) -> Option<ConnectionData> {
self.visible_connections.remove(&connection_id)
}
fn add_channel(&mut self, channel_id: ChannelId) -> Result<&mut ChannelData, Error> {
match self.channels {
Ok(ref mut cs) => {
let mut channel = ChannelData::new(self.id, channel_id);
channel.update();
cs.insert(channel_id, channel);
Ok(cs.get_mut(&channel_id).unwrap())
}
Err(error) => Err(error),
}
}
fn remove_channel(&mut self, channel_id: ChannelId) -> Option<ChannelData> {
self.channels.as_mut().ok().and_then(|cs| cs.remove(&channel_id))
}
fn get_mut_connection(&mut self, connection_id: ConnectionId) -> Option<&mut ConnectionData> {
self.visible_connections.get_mut(&connection_id)
}
fn get_mut_channel(&mut self, channel_id: ChannelId) -> Option<&mut ChannelData> {
self.channels.as_mut().ok().and_then(|cs| cs.get_mut(&channel_id))
}
}
impl<'a> Server<'a> {
fn new(api: &'a TsApi, data: &'a ServerData) -> Server<'a> {
Server { api, data: Ok(data) }
}
fn new_err(api: &'a TsApi, server_id: ServerId) -> Server<'a> {
Server { api, data: Err(server_id) }
}
pub fn get_id(&self) -> ServerId {
match self.data {
Ok(data) => data.get_id(),
Err(id) => id,
}
}
fn get_connection_unwrap(&self, connection_id: ConnectionId) -> Connection<'a> {
self.get_connection(connection_id).unwrap_or_else(|| {
self.api.log_or_print(
format!("Can't find connection {:?}", connection_id),
"rust-ts3plugin",
crate::LogLevel::Warning,
);
Connection::new_err(&self.api, self.get_id(), connection_id)
})
}
fn get_channel_unwrap(&self, channel_id: ChannelId) -> Channel<'a> {
self.get_channel(channel_id).unwrap_or_else(|| {
self.api.log_or_print(
format!("Can't find channel {:?}", channel_id),
"rust-ts3plugin",
crate::LogLevel::Warning,
);
Channel::new_owned(&self.api, self.get_id(), channel_id)
})
}
fn get_server_group_unwrap(&self, server_group_id: ServerGroupId) -> ServerGroup {
self.get_server_group(server_group_id).unwrap_or_else(|| {
ServerGroup {}
})
}
fn get_channel_group_unwrap(&self, channel_group_id: ChannelGroupId) -> ChannelGroup {
self.get_channel_group(channel_group_id).unwrap_or_else(|| {
ChannelGroup {}
})
}
pub fn get_own_connection(&self) -> Result<Connection<'a>, Error> {
match self.data {
Ok(data) => data.get_own_connection_id().map(|id| self.get_connection_unwrap(id)),
Err(_) => Err(Error::Ok),
}
}
pub fn get_connections(&self) -> Vec<Connection<'a>> {
match self.data {
Ok(data) => {
data.visible_connections.values().map(|c| Connection::new(self.api, &c)).collect()
}
Err(_) => Vec::new(),
}
}
pub fn get_channels(&self) -> Vec<Channel<'a>> {
match self.data {
Ok(data) => match data.channels {
Ok(ref cs) => cs.values().map(|c| Channel::new(self.api, &c)).collect(),
Err(_) => Vec::new(),
},
Err(_) => Vec::new(),
}
}
pub fn get_connection(&self, connection_id: ConnectionId) -> Option<Connection<'a>> {
self.data.ok().and_then(|data| {
data.visible_connections.get(&connection_id).map(|c| Connection::new(&self.api, c))
})
}
pub fn get_channel(&self, channel_id: ChannelId) -> Option<Channel<'a>> {
self.data.ok().and_then(|data| {
data.channels
.as_ref()
.ok()
.and_then(|cs| cs.get(&channel_id))
.map(|c| Channel::new(&self.api, c))
})
}
pub fn get_server_group(&self, _server_group_id: ServerGroupId) -> Option<ServerGroup> {
Some(ServerGroup {})
}
pub fn get_channel_group(&self, _channel_group_id: ChannelGroupId) -> Option<ChannelGroup> {
Some(ChannelGroup {})
}
pub fn send_message<S: AsRef<str>>(&self, message: S) -> Result<(), Error> {
unsafe {
let text = to_cstring!(message.as_ref());
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.request_send_server_text_msg)(
self.get_id().0, text.as_ptr(), std::ptr::null()
));
match res {
Error::Ok => Ok(()),
_ => Err(res),
}
}
}
pub fn send_plugin_message<S: AsRef<str>>(&self, message: S) {
let text = to_cstring!(message.as_ref());
(TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.send_plugin_command)(
self.get_id().0,
to_cstring!(self.api.get_plugin_id()).as_ptr(),
text.as_ptr(),
PluginTargetMode::Server as i32,
std::ptr::null(),
std::ptr::null(),
);
}
pub fn print_message<S: AsRef<str>>(&self, message: S, target: MessageTarget) {
let text = to_cstring!(message.as_ref());
(TS3_FUNCTIONS.read().unwrap().as_ref().expect("Functions should be loaded").print_message)(
self.get_id().0,
text.as_ptr(),
target,
);
}
}
#[derive(Clone)]
pub struct Channel<'a> {
api: &'a TsApi,
data: Result<&'a ChannelData, (ServerId, ChannelId)>,
}
impl<'a, 'b> PartialEq<Channel<'b>> for Channel<'a> {
fn eq(&self, other: &Channel<'b>) -> bool {
self.get_server_id() == other.get_server_id() && self.get_id() == other.get_id()
}
}
impl<'a> Eq for Channel<'a> {}
impl<'a> fmt::Debug for Channel<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Channel({})", self.get_id().0)
}
}
impl PartialEq<ChannelData> for ChannelData {
fn eq(&self, other: &ChannelData) -> bool {
self.server_id == other.server_id && self.id == other.id
}
}
impl Eq for ChannelData {}
impl ChannelData {
fn get_property_as_string(
server_id: ServerId, id: ChannelId, property: ChannelProperties,
) -> Result<String, Error> {
unsafe {
let mut name: *mut c_char = std::ptr::null_mut();
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_channel_variable_as_string)(
server_id.0, id.0, property as usize, &mut name
));
match res {
Error::Ok => Ok(to_string!(name)),
_ => Err(res),
}
}
}
fn get_property_as_int(
server_id: ServerId, id: ChannelId, property: ChannelProperties,
) -> Result<i32, Error> {
unsafe {
let mut number: c_int = 0;
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_channel_variable_as_int)(
server_id.0, id.0, property as usize, &mut number
));
match res {
Error::Ok => Ok(number as i32),
_ => Err(res),
}
}
}
fn get_property_as_uint64(
server_id: ServerId, id: ChannelId, property: ChannelProperties,
) -> Result<i32, Error> {
unsafe {
let mut number: u64 = 0;
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_channel_variable_as_uint64)(
server_id.0, id.0, property as usize, &mut number
));
match res {
Error::Ok => Ok(number as i32),
_ => Err(res),
}
}
}
fn query_parent_channel_id(server_id: ServerId, id: ChannelId) -> Result<ChannelId, Error> {
unsafe {
let mut number: u64 = 0;
let res: Error =
transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_parent_channel_of_channel)(server_id.0, id.0, &mut number));
match res {
Error::Ok => Ok(ChannelId(number)),
_ => Err(res),
}
}
}
}
impl<'a> Channel<'a> {
fn new(api: &'a TsApi, data: &'a ChannelData) -> Channel<'a> {
Channel { api, data: Ok(data) }
}
fn new_owned(api: &'a TsApi, server_id: ServerId, channel_id: ChannelId) -> Channel<'a> {
Channel { api, data: Err((server_id, channel_id)) }
}
fn get_server_id(&self) -> ServerId {
match self.data {
Ok(data) => data.get_server_id(),
Err((server_id, _)) => server_id,
}
}
pub fn get_id(&self) -> ChannelId {
match self.data {
Ok(data) => data.get_id(),
Err((_, channel_id)) => channel_id,
}
}
pub fn get_server(&self) -> Server<'a> {
self.api.get_server_unwrap(self.get_server_id())
}
pub fn get_parent_channel(&self) -> Result<Option<Channel<'a>>, Error> {
match self.data {
Ok(data) => data.get_parent_channel_id().map(|parent_channel_id| {
if parent_channel_id.0 == 0 {
None
} else {
Some(self.get_server().get_channel_unwrap(parent_channel_id))
}
}),
Err(_) => Err(Error::Ok),
}
}
pub fn send_message<S: AsRef<str>>(&self, message: S) -> Result<(), Error> {
unsafe {
let text = to_cstring!(message.as_ref());
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.request_send_channel_text_msg)(
self.data.unwrap().server_id.0,
text.as_ptr(),
self.data.unwrap().id.0,
std::ptr::null(),
));
match res {
Error::Ok => Ok(()),
_ => Err(res),
}
}
}
}
#[derive(Clone)]
pub struct Connection<'a> {
api: &'a TsApi,
data: Result<&'a ConnectionData, (ServerId, ConnectionId)>,
}
impl<'a, 'b> PartialEq<Connection<'b>> for Connection<'a> {
fn eq(&self, other: &Connection<'b>) -> bool {
self.get_server_id() == other.get_server_id() && self.get_id() == other.get_id()
}
}
impl<'a> Eq for Connection<'a> {}
impl<'a> fmt::Debug for Connection<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Connection({})", self.get_id().0)
}
}
impl PartialEq<ConnectionData> for ConnectionData {
fn eq(&self, other: &ConnectionData) -> bool {
self.server_id == other.server_id && self.id == other.id
}
}
impl Eq for ConnectionData {}
impl ConnectionData {
fn get_connection_property_as_string(
server_id: ServerId, id: ConnectionId, property: ConnectionProperties,
) -> Result<String, Error> {
unsafe {
let mut name: *mut c_char = std::ptr::null_mut();
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_connection_variable_as_string)(
server_id.0, id.0, property as usize, &mut name
));
match res {
Error::Ok => Ok(to_string!(name)),
_ => Err(res),
}
}
}
fn get_connection_property_as_uint64(
server_id: ServerId, id: ConnectionId, property: ConnectionProperties,
) -> Result<u64, Error> {
unsafe {
let mut number: u64 = 0;
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_connection_variable_as_uint64)(
server_id.0, id.0, property as usize, &mut number
));
match res {
Error::Ok => Ok(number),
_ => Err(res),
}
}
}
fn get_connection_property_as_double(
server_id: ServerId, id: ConnectionId, property: ConnectionProperties,
) -> Result<f64, Error> {
unsafe {
let mut number: f64 = 0.0;
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_connection_variable_as_double)(
server_id.0, id.0, property as usize, &mut number
));
match res {
Error::Ok => Ok(number),
_ => Err(res),
}
}
}
fn get_client_property_as_string(
server_id: ServerId, id: ConnectionId, property: ClientProperties,
) -> Result<String, Error> {
unsafe {
let mut name: *mut c_char = std::ptr::null_mut();
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_client_variable_as_string)(
server_id.0, id.0, property as usize, &mut name
));
match res {
Error::Ok => Ok(to_string!(name)),
_ => Err(res),
}
}
}
fn get_client_property_as_int(
server_id: ServerId, id: ConnectionId, property: ClientProperties,
) -> Result<c_int, Error> {
unsafe {
let mut number: c_int = 0;
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_client_variable_as_int)(
server_id.0, id.0, property as usize, &mut number
));
match res {
Error::Ok => Ok(number),
_ => Err(res),
}
}
}
fn query_channel_id(server_id: ServerId, id: ConnectionId) -> Result<ChannelId, Error> {
unsafe {
let mut number: u64 = 0;
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_channel_of_client)(server_id.0, id.0, &mut number));
match res {
Error::Ok => Ok(ChannelId(number)),
_ => Err(res),
}
}
}
fn query_whispering(server_id: ServerId, id: ConnectionId) -> Result<bool, Error> {
unsafe {
let mut number: c_int = 0;
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.is_whispering)(server_id.0, id.0, &mut number));
match res {
Error::Ok => Ok(number != 0),
_ => Err(res),
}
}
}
}
impl<'a> Connection<'a> {
fn new(api: &'a TsApi, data: &'a ConnectionData) -> Connection<'a> {
Connection { api, data: Ok(data) }
}
fn new_err(api: &'a TsApi, server_id: ServerId, connection_id: ConnectionId) -> Connection<'a> {
Connection { api, data: Err((server_id, connection_id)) }
}
fn get_server_id(&self) -> ServerId {
match self.data {
Ok(data) => data.get_server_id(),
Err((server_id, _)) => server_id,
}
}
pub fn get_id(&self) -> ConnectionId {
match self.data {
Ok(data) => data.get_id(),
Err((_, connection_id)) => connection_id,
}
}
pub fn get_server(&self) -> Server<'a> {
self.api.get_server_unwrap(self.get_server_id())
}
pub fn get_channel(&self) -> Result<Channel<'a>, Error> {
match self.data {
Ok(data) => data.get_channel_id().map(|c| self.get_server().get_channel_unwrap(c)),
Err(_) => Err(Error::Ok),
}
}
pub fn get_channel_group_inherited_channel(&self) -> Result<Channel<'a>, Error> {
match self.data {
Ok(data) => data
.get_channel_group_inherited_channel_id()
.map(|c| self.get_server().get_channel_unwrap(c)),
Err(_) => Err(Error::Ok),
}
}
pub fn send_message<S: AsRef<str>>(&self, message: S) -> Result<(), Error> {
unsafe {
let text = to_cstring!(message.as_ref());
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.request_send_private_text_msg)(
self.data.unwrap().server_id.0,
text.as_ptr(),
self.data.unwrap().id.0,
std::ptr::null(),
));
match res {
Error::Ok => Ok(()),
_ => Err(res),
}
}
}
}
pub struct TsApiLock {
guard: MutexGuard<'static, (Option<(TsApi, Box<dyn Plugin>)>, Option<String>)>,
}
impl Deref for TsApiLock {
type Target = TsApi;
fn deref(&self) -> &Self::Target {
&self.guard.0.as_ref().unwrap().0
}
}
impl DerefMut for TsApiLock {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.guard.0.as_mut().unwrap().0
}
}
pub struct PluginLock {
guard: MutexGuard<'static, (Option<(TsApi, Box<dyn Plugin>)>, Option<String>)>,
}
impl Deref for PluginLock {
type Target = dyn Plugin;
fn deref(&self) -> &Self::Target {
&*self.guard.0.as_ref().unwrap().1
}
}
impl DerefMut for PluginLock {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.guard.0.as_mut().unwrap().1
}
}
pub struct TsApi {
servers: Map<ServerId, ServerData>,
plugin_id: String,
}
impl TsApi {
fn new(plugin_id: String) -> TsApi {
TsApi { servers: Map::new(), plugin_id: plugin_id }
}
fn load(&mut self) -> Result<(), Error> {
let mut result: *mut u64 = std::ptr::null_mut();
let res: Error = unsafe {
transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_server_connection_handler_list)(&mut result))
};
match res {
Error::Ok => unsafe {
let mut counter = 0;
while *result.offset(counter) != 0 {
let mut status: c_int = 0;
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_connection_status)(
*result.offset(counter), &mut status
));
if res == Error::Ok
&& transmute::<c_int, ConnectStatus>(status) != ConnectStatus::Disconnected
{
self.add_server(ServerId(*result.offset(counter)));
}
counter += 1;
}
},
_ => return Err(res),
}
Ok(())
}
pub fn lock_api() -> Option<TsApiLock> {
let guard = ts3interface::DATA.lock().unwrap();
if guard.0.is_none() { None } else { Some(TsApiLock { guard }) }
}
pub fn lock_plugin() -> Option<PluginLock> {
let guard = ts3interface::DATA.lock().unwrap();
if guard.0.is_none() { None } else { Some(PluginLock { guard }) }
}
pub fn static_log_message<S1: AsRef<str>, S2: AsRef<str>>(
message: S1, channel: S2, severity: LogLevel,
) -> Result<(), Error> {
unsafe {
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.log_message)(
to_cstring!(message.as_ref()).as_ptr(),
severity,
to_cstring!(channel.as_ref()).as_ptr(),
0,
));
match res {
Error::Ok => Ok(()),
_ => Err(res),
}
}
}
pub fn static_log_or_print<S1: AsRef<str>, S2: AsRef<str>>(
message: S1, channel: S2, severity: LogLevel,
) {
if let Err(error) = TsApi::static_log_message(message.as_ref(), channel.as_ref(), severity)
{
println!(
"Error {:?} while printing '{}' to '{}' ({:?})",
error,
message.as_ref(),
channel.as_ref(),
severity
);
}
}
pub fn static_get_error_message(error: Error) -> Result<String, Error> {
unsafe {
let mut message: *mut c_char = std::ptr::null_mut();
let res: Error = transmute((TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_error_message)(error as u32, &mut message));
match res {
Error::Ok => Ok(to_string!(message)),
_ => Err(res),
}
}
}
fn add_server(&mut self, server_id: ServerId) -> &mut ServerData {
self.servers.insert(server_id, ServerData::new(server_id));
let server = self.servers.get_mut(&server_id).unwrap();
server.update();
server
}
fn remove_server(&mut self, server_id: ServerId) -> Option<ServerData> {
self.servers.remove(&server_id)
}
pub fn get_plugin_id(&self) -> &str {
&self.plugin_id
}
fn try_update_invoker(&mut self, server_id: ServerId, invoker: &InvokerData) {
if let Some(server) = self.get_mut_server(server_id) {
if let Some(connection) = server.get_mut_connection(invoker.get_id()) {
if connection.get_uid() != Ok(invoker.get_uid()) {
connection.uid = Ok(invoker.get_uid().clone());
}
if connection.get_name() != Ok(invoker.get_name()) {
connection.name = Ok(invoker.get_name().clone())
}
}
}
}
fn get_path<F: Fn(*mut c_char, usize)>(fun: F) -> String {
const START_SIZE: usize = 512;
const MAX_SIZE: usize = 100_000;
let mut size = START_SIZE;
loop {
let mut buf = vec![0 as u8; size];
fun(buf.as_mut_ptr() as *mut c_char, size - 1);
if buf[size - 3] != 0 {
size *= 2;
if size > MAX_SIZE {
return String::new();
}
} else {
buf[size - 1] = 0;
let s = unsafe { CStr::from_ptr(buf.as_ptr() as *const c_char) };
let result = s.to_string_lossy();
return result.into_owned();
}
}
}
fn get_mut_server(&mut self, server_id: ServerId) -> Option<&mut ServerData> {
self.servers.get_mut(&server_id)
}
fn get_server_unwrap<'a>(&'a self, server_id: ServerId) -> Server<'a> {
self.servers.get(&server_id).map(|s| Server::<'a>::new(&self, s)).unwrap_or_else(|| {
Server::new_err(&self, server_id)
})
}
pub fn get_servers<'a>(&'a self) -> Vec<Server<'a>> {
self.servers.values().map(|s| Server::new(&self, &s)).collect()
}
pub fn log_message<S1: AsRef<str>, S2: AsRef<str>>(
&self, message: S1, channel: S2, severity: LogLevel,
) -> Result<(), Error> {
TsApi::static_log_message(message, channel, severity)
}
pub fn log_or_print<S1: AsRef<str>, S2: AsRef<str>>(
&self, message: S1, channel: S2, severity: LogLevel,
) {
TsApi::static_log_or_print(message, channel, severity)
}
pub fn get_server(&'_ self, server_id: ServerId) -> Option<Server<'_>> {
self.servers.get(&server_id).map(|s| Server::new(&self, s))
}
pub fn get_permission(&self, _permission_id: PermissionId) -> Option<&Permission> {
Some(&Permission {})
}
pub fn print_message<S: AsRef<str>>(&self, message: S) {
let text = to_cstring!(message.as_ref());
(TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.print_message_to_current_tab)(text.as_ptr());
}
pub fn get_app_path(&self) -> String {
TsApi::get_path(|p, l| {
(TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_app_path)(p, l)
})
}
pub fn get_resources_path(&self) -> String {
TsApi::get_path(|p, l| {
(TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_resources_path)(p, l)
})
}
pub fn get_config_path(&self) -> String {
TsApi::get_path(|p, l| {
(TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_config_path)(p, l)
})
}
pub fn get_plugin_path(&self) -> String {
TsApi::get_path(|p, l| {
(TS3_FUNCTIONS
.read()
.unwrap()
.as_ref()
.expect("Functions should be loaded")
.get_plugin_path)(p, l, to_cstring!(self.plugin_id.as_str()).as_ptr())
})
}
}