use crate::{from_cstring, ChannelRef, IrcIdent, IrcIdentRef, UserString};
use std::os::raw::c_char;
pub trait ServerEvent {
const NAME: &'static str;
#[doc(hidden)]
unsafe fn create(word: *mut *mut c_char, word_eol: *mut *mut c_char) -> Self;
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PRIVMSG {
user: UserString,
target: PrivmsgTarget,
message: String,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum PrivmsgTarget {
User(IrcIdent),
Channel {
channel_name: IrcIdent,
channel: ChannelRef,
},
HostMask(IrcIdent),
ServerMask(IrcIdent),
}
impl PRIVMSG {
pub fn get_user(&self) -> &UserString {
&self.user
}
pub fn get_target(&self) -> &PrivmsgTarget {
&self.target
}
pub fn get_message(&self) -> &str {
&self.message
}
}
impl ServerEvent for PRIVMSG {
const NAME: &'static str = "PRIVMSG";
unsafe fn create(word: *mut *mut c_char, word_eol: *mut *mut c_char) -> Self {
let arg1 = *word.offset(1);
let user_string = from_cstring(arg1.offset(1));
let user = UserString::new(user_string).unwrap();
let arg3 = *word.offset(3);
let target = match *arg3 as u8 {
b'#' => {
let mut target_string = IrcIdent(from_cstring(arg3));
if target_string.contains('*') {
target_string.0.remove(1);
PrivmsgTarget::HostMask(target_string)
} else {
let channel = crate::get_server_name()
.and_then(|s| crate::get_channel(&s, &target_string))
.unwrap_or_else(|| crate::get_first_channel(&target_string).unwrap());
PrivmsgTarget::Channel {
channel,
channel_name: target_string,
}
}
}
b'$' => PrivmsgTarget::ServerMask(IrcIdent(from_cstring(arg3.offset(1)))),
_ => PrivmsgTarget::User(IrcIdent(from_cstring(arg3))),
};
let arg4_eol = *word_eol.offset(4);
let message = from_cstring(arg4_eol.offset(1));
Self {
user,
target,
message,
}
}
}
pub struct JOIN {
user: UserString,
channel_string: IrcIdent,
channel: ChannelRef,
}
impl JOIN {
pub fn get_user(&self) -> &UserString {
&self.user
}
pub fn get_channel_name(&self) -> IrcIdentRef {
self.channel_string.as_ref()
}
pub fn get_channel(&self) -> &ChannelRef {
&self.channel
}
}
impl ServerEvent for JOIN {
const NAME: &'static str = "JOIN";
unsafe fn create(word: *mut *mut c_char, _word_eol: *mut *mut c_char) -> Self {
let arg1 = *word.offset(1);
let user_string = from_cstring(arg1.offset(1));
let user = UserString::new(user_string).unwrap();
let arg3 = *word.offset(3);
let channel_string = IrcIdent(from_cstring(arg3.offset(1)));
let channel = crate::get_server_name()
.and_then(|s| crate::get_channel(&s, &channel_string))
.unwrap_or_else(|| crate::get_first_channel(&channel_string).unwrap());
Self {
user,
channel_string,
channel,
}
}
}
pub struct QUIT {
user: UserString,
message: Option<String>,
}
impl QUIT {
pub fn get_user(&self) -> &UserString {
&self.user
}
pub fn get_message(&self) -> Option<&str> {
self.message.as_ref().map(|s| &**s)
}
}
impl ServerEvent for QUIT {
const NAME: &'static str = "QUIT";
unsafe fn create(word: *mut *mut c_char, word_eol: *mut *mut c_char) -> Self {
let arg1 = *word.offset(1);
let user_string = from_cstring(arg1.offset(1));
let user = UserString::new(user_string).unwrap();
let arg3_eol_ptr = word_eol.offset(3);
let message = if arg3_eol_ptr.is_null() {
None
} else {
let arg3_eol = *arg3_eol_ptr;
if arg3_eol.is_null() || *arg3_eol == b'\0' as _ || *arg3_eol.offset(1) == b'\0' as _ {
None
} else {
Some(from_cstring(arg3_eol.offset(1)))
}
};
Self { user, message }
}
}
pub struct PART {
user: UserString,
channel_names: Vec<IrcIdent>,
channels: Vec<ChannelRef>,
message: Option<String>,
}
impl PART {
pub fn get_user(&self) -> &UserString {
&self.user
}
pub fn get_channel_names(&self) -> &[IrcIdent] {
&self.channel_names
}
pub fn get_channels(&self) -> &[ChannelRef] {
&self.channels
}
pub fn get_message(&self) -> Option<&str> {
self.message.as_ref().map(|s| &**s)
}
}
impl ServerEvent for PART {
const NAME: &'static str = "PART";
unsafe fn create(word: *mut *mut c_char, _word_eol: *mut *mut c_char) -> Self {
let arg1 = *word.offset(1);
let user_string = from_cstring(arg1.offset(1));
let user = UserString::new(user_string).unwrap();
let arg3 = *word.offset(3);
let channel_string = from_cstring(arg3);
let channel_names: Vec<_> = channel_string
.split(',')
.map(|t| IrcIdent(t.to_string()))
.collect();
let server = crate::get_server_name();
let channels = channel_names
.iter()
.map(|c| {
server
.as_ref()
.and_then(|s| crate::get_channel(s, c))
.unwrap_or_else(|| crate::get_first_channel(c).unwrap())
})
.collect();
let arg4_eol_ptr = word.offset(4);
let message = if arg4_eol_ptr.is_null() {
None
} else {
let arg4_eol = *arg4_eol_ptr;
if arg4_eol.is_null() || *arg4_eol == b'\0' as _ || *arg4_eol.offset(1) == b'\0' as _ {
None
} else {
Some(from_cstring(arg4_eol.offset(1)))
}
};
Self {
user,
channel_names,
channels,
message,
}
}
}
pub struct TOPIC {
user: UserString,
channel_string: IrcIdent,
channel: ChannelRef,
message: Option<String>,
}
impl TOPIC {
pub fn get_user(&self) -> &UserString {
&self.user
}
pub fn get_channel_name(&self) -> IrcIdentRef {
self.channel_string.as_ref()
}
pub fn get_channel(&self) -> &ChannelRef {
&self.channel
}
pub fn get_message(&self) -> Option<&str> {
self.message.as_ref().map(|s| &**s)
}
}
impl ServerEvent for TOPIC {
const NAME: &'static str = "TOPIC";
unsafe fn create(word: *mut *mut c_char, word_eol: *mut *mut c_char) -> Self {
let arg1 = *word.offset(1);
let user_string = from_cstring(arg1.offset(1));
let user = UserString::new(user_string).unwrap();
let arg3 = *word.offset(3);
let channel_string = IrcIdent(from_cstring(arg3));
let channel = crate::get_server_name()
.and_then(|s| crate::get_channel(&s, &channel_string))
.unwrap_or_else(|| crate::get_first_channel(&channel_string).unwrap());
let arg4_eol_ptr = word_eol.offset(4);
let message = if arg4_eol_ptr.is_null() {
None
} else {
let arg4_eol = *arg4_eol_ptr;
if arg4_eol.is_null() || *arg4_eol == b'\0' as _ || *arg4_eol.offset(1) == b'\0' as _ {
None
} else {
Some(from_cstring(arg4_eol.offset(1)))
}
};
Self {
user,
channel_string,
channel,
message,
}
}
}
pub struct INVITE {
sender: UserString,
recipient: IrcIdent,
channel_string: IrcIdent,
channel: ChannelRef,
}
impl INVITE {
pub fn get_sender(&self) -> &UserString {
&self.sender
}
pub fn get_recipient(&self) -> IrcIdentRef {
self.recipient.as_ref()
}
pub fn get_channel_name(&self) -> IrcIdentRef {
self.channel_string.as_ref()
}
pub fn get_channel(&self) -> &ChannelRef {
&self.channel
}
}
impl ServerEvent for INVITE {
const NAME: &'static str = "INVITE";
unsafe fn create(word: *mut *mut c_char, _word_eol: *mut *mut c_char) -> Self {
let arg1 = *word.offset(1);
let sender_string = from_cstring(arg1.offset(1));
let sender = UserString::new(sender_string).unwrap();
let arg3 = *word.offset(3);
let recipient = IrcIdent(from_cstring(arg3));
let arg4 = *word.offset(4);
let channel_string = IrcIdent(from_cstring(arg4));
let channel = crate::get_server_name()
.and_then(|s| crate::get_channel(&s, &channel_string))
.unwrap_or_else(|| crate::get_first_channel(&channel_string).unwrap());
Self {
sender,
recipient,
channel_string,
channel,
}
}
}
pub struct KICK {
sender: UserString,
channel: ChannelRef,
channel_string: IrcIdent,
kicked: IrcIdent,
comment: Option<String>,
}
impl KICK {
pub fn get_sender(&self) -> &UserString {
&self.sender
}
pub fn get_channel(&self) -> &ChannelRef {
&self.channel
}
pub fn get_channel_name(&self) -> IrcIdentRef {
self.channel_string.as_ref()
}
pub fn get_kicked(&self) -> IrcIdentRef {
self.kicked.as_ref()
}
pub fn get_comment(&self) -> Option<&str> {
self.comment.as_ref().map(|s| &**s)
}
}
impl ServerEvent for KICK {
const NAME: &'static str = "KICK";
unsafe fn create(word: *mut *mut c_char, word_eol: *mut *mut c_char) -> Self {
let arg1 = *word.offset(1);
let sender_string = from_cstring(arg1.offset(1));
let sender = UserString::new(sender_string).unwrap();
let arg3 = *word.offset(3);
let channel_string = IrcIdent(from_cstring(arg3));
let channel = crate::get_server_name()
.and_then(|s| crate::get_channel(&s, &channel_string))
.unwrap_or_else(|| crate::get_first_channel(&channel_string).unwrap());
let arg4 = *word.offset(4);
let kicked = IrcIdent(from_cstring(arg4));
let arg5_eol_ptr = word_eol.offset(5);
let comment = if arg5_eol_ptr.is_null() {
None
} else {
let arg5_eol = *arg5_eol_ptr;
if arg5_eol.is_null() || *arg5_eol == b'\0' as _ || *arg5_eol.offset(1) == b'\0' as _ {
None
} else {
Some(from_cstring(arg5_eol.offset(1)))
}
};
Self {
sender,
channel,
channel_string,
comment,
kicked,
}
}
}
pub struct NOTICE {
privmsg: PRIVMSG,
}
impl NOTICE {
pub fn get_user(&self) -> &UserString {
self.privmsg.get_user()
}
pub fn get_target(&self) -> &PrivmsgTarget {
self.privmsg.get_target()
}
pub fn get_message(&self) -> &str {
self.privmsg.get_message()
}
}
impl ServerEvent for NOTICE {
const NAME: &'static str = "NOTICE";
unsafe fn create(word: *mut *mut c_char, word_eol: *mut *mut c_char) -> Self {
Self {
privmsg: PRIVMSG::create(word, word_eol),
}
}
}
pub struct WALLOPS {
server_name: IrcIdent,
message: String,
}
impl WALLOPS {
pub fn get_server_name(&self) -> IrcIdentRef {
self.server_name.as_ref()
}
pub fn get_message(&self) -> &str {
&self.message
}
}
impl ServerEvent for WALLOPS {
const NAME: &'static str = "WALLOPS";
unsafe fn create(word: *mut *mut c_char, word_eol: *mut *mut c_char) -> Self {
let arg1 = *word.offset(1);
let server_name = IrcIdent(from_cstring(arg1.offset(1)));
let arg3 = *word_eol.offset(3);
let message = from_cstring(arg3.offset(1));
Self {
server_name,
message,
}
}
}