// This file is part of Hexchat Plugin API Bindings for Rust
// Copyright (C) 2022 Soni L.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//! List support module.
use std::borrow::Cow;
use std::cell::Cell;
use std::ffi::CStr;
use std::rc::Rc;
use crate::Context;
use crate::wrap_context;
mod sealed {
pub trait Sealed {
}
}
/// A list. This trait is sealed.
pub trait List: sealed::Sealed {
fn name(&self) -> Cow<'static, str>;
}
// List types
/// A "channels" list.
pub struct Contexts;
/// A "dcc" list.
pub struct Dcc;
/// An "ignore" list.
pub struct Ignore;
/// A "notify" list.
pub struct Notify;
/// An "users" list.
pub struct Users;
/// Entries.
pub struct Entries<'a, 'ph, T> {
pub(crate) context: &'a crate::PluginHandle<'ph>,
pub(crate) list: *mut crate::internals::HexchatList,
pub(crate) t: &'a T,
pub(crate) valid: Rc>,
}
/// Fields.
pub struct Fields<'a, 'ph, T> {
pub(crate) context: &'a crate::PluginHandle<'ph>,
pub(crate) list: *mut crate::internals::HexchatList,
pub(crate) _t: &'a T,
pub(crate) id: usize,
pub(crate) valid: Rc>,
}
// impls
impl sealed::Sealed for Contexts {}
impl sealed::Sealed for Dcc {}
impl sealed::Sealed for Ignore {}
impl sealed::Sealed for Notify {}
impl sealed::Sealed for Users {}
impl List for Contexts {
fn name(&self) -> Cow<'static, str> { "channels".into() }
}
impl List for Dcc {
fn name(&self) -> Cow<'static, str> { "dcc".into() }
}
impl List for Ignore {
fn name(&self) -> Cow<'static, str> { "ignore".into() }
}
impl List for Notify {
fn name(&self) -> Cow<'static, str> { "notify".into() }
}
impl List for Users {
fn name(&self) -> Cow<'static, str> { "users".into() }
}
impl<'a, 'ph, T> Drop for Entries<'a, 'ph, T> {
fn drop(&mut self) {
let ph = &self.context;
self.valid.set(usize::MAX);
unsafe {
ph_call!(hexchat_list_free(ph, self.list));
}
}
}
impl<'a, 'ph, T> Iterator for Entries<'a, 'ph, T> {
type Item = Fields<'a, 'ph, T>;
fn next(&mut self) -> Option {
let ph = &self.context;
let id = self.valid.get();
if unsafe {
ph_call!(hexchat_list_next(ph, self.list))
} != 0 {
debug_assert!(self.valid.get() < usize::MAX);
self.valid.set(id + 1);
Some(Fields {
context: self.context,
list: self.list,
_t: self.t,
valid: Rc::clone(&self.valid),
id: id,
})
} else {
self.valid.set(usize::MAX);
None
}
}
}
macro_rules! field {
($(#[$m:meta])* $f:ident, unsafe {$n:expr}, $r:ty, $ph:ident, $c:ident, $($conv:tt)+) => {
$(#[$m])* pub fn $f(&self) -> $r {
assert_eq!(
self.id, self.valid.get(),
"instances of Fields are invalidated by next()",
);
let $ph = &self.context;
const NAME: &'static CStr = unsafe {
CStr::from_bytes_with_nul_unchecked(
$n.as_bytes()
)
};
match unsafe {
ph_call!($c($ph, self.list, NAME.as_ptr()))
} {
$($conv)+
}
}
};
($(#[$m:meta])* $f:ident, $r:ty, $ph:ident, $c:ident, $($conv:tt)+) => {
field!(
$(#[$m])* $f,
unsafe { ::std::concat!(::std::stringify!($f), "\0") },
$r,
$ph,
$c,
$($conv)+
);
};
}
macro_rules! field_int {
($(#[$m:meta])* $f:ident, unsafe { $n:expr }) => {
field!($(#[$m])* $f, unsafe { $n }, i32, ph, hexchat_list_int, r => r as i32);
};
($(#[$m:meta])* $f:ident) => {
field_int!($(#[$m])* $f, unsafe { ::std::concat!(::std::stringify!($f), "\0") });
};
}
macro_rules! field_bool {
($(#[$m:meta])* $f:ident, unsafe { $n:expr }) => {
field!($(#[$m])* $f, unsafe { $n }, bool, ph, hexchat_list_int, r => r != 0);
};
($(#[$m:meta])* $f:ident) => {
field_bool!($(#[$m])* $f, unsafe { ::std::concat!(::std::stringify!($f), "\0") });
};
}
macro_rules! field_str {
($(#[$m:meta])* $f:ident, unsafe { $n:expr }) => {
field!(
$(#[$m])* $f, unsafe { $n }, Option, ph, hexchat_list_str,
r if r.is_null() => {
None
},
r => Some(unsafe {
CStr::from_ptr(r)
}.to_owned().into_string().unwrap())
);
};
($(#[$m:meta])* $f:ident) => {
field_str!($(#[$m])* $f, unsafe { ::std::concat!(::std::stringify!($f), "\0") });
};
}
macro_rules! field_time {
($(#[$m:meta])* $f:ident) => {
$(#[$m])* pub fn $f(&self) -> ! {
todo!()
}
};
}
/// Contexts fields.
impl<'a, 'ph> Fields<'a, 'ph, Contexts> {
field_str!(
/// The context's name.
name, unsafe { "channel" }
);
field_str!(
/// The channel key.
channelkey
);
field_str!(
/// The server's channel modes. Requires HexChat 2.12.2+.
chanmodes
);
field_str!(
/// The server's channel types.
chantypes
);
field!(
/// The context.
context, Context<'ph>, ph, hexchat_list_str, r => unsafe {
wrap_context(ph, r as *const _)
}
);
field_int!(
/// Raw flags about this context.
raw_flags, unsafe { "flags" }
);
field_int!(
/// The server ID.
server_id, unsafe { "id" }
);
field_int!(
/// The latency to server in milliseconds.
lag
);
field_int!(
/// The maximum number of mode changes per line accepted by the server.
maxmodes
);
field_str!(
/// The network name.
network
);
field_str!(
/// The server's nick prefixes.
nickprefixes
);
field_str!(
/// The server's nick modes.
nickmodes
);
field_int!(
/// The current length of the send-queue, in bytes.
queue_len, unsafe { "queue" }
);
field_str!(
/// The server's name.
server
);
field_int!(
/// The context's raw type.
raw_type, unsafe { "type" }
);
field_int!(
/// The number of users in the channel.
users
);
}
/// Dcc fields.
impl<'a, 'ph> Fields<'a, 'ph, Dcc> {
field_int!(
/// The raw 32-bit IPv4 address of the remote user.
address32
);
field_int!(
/// The transfer rate (speed), in bytes per second.
rate, unsafe { "cps" }
);
field_str!(
/// The destination file path.
destfile
);
field_str!(
/// The filename.
file
);
field_str!(
/// The remote user's nick.
nick
);
field_int!(
/// The listener's port number.
port
);
field_int!(
/// File position, LSB 32 bits.
raw_pos, unsafe { "pos" }
);
field_int!(
/// File position, MSB 32 bits.
raw_poshigh, unsafe { "poshigh" }
);
field_int!(
/// Resumed position, LSB 32 bits.
raw_resume, unsafe { "resume" }
);
field_int!(
/// Resumed position, MSB 32 bits.
raw_resumehigh, unsafe { "resumehigh" }
);
field_int!(
/// File size, LSB 32 bits.
raw_size, unsafe { "size" }
);
field_int!(
/// File size, MSB 32 bits.
raw_sizehigh, unsafe { "sizehigh" }
);
field_int!(
/// Raw DCC status.
raw_status, unsafe { "status" }
);
field_int!(
/// Raw type.
raw_type, unsafe { "type" }
);
}
/// Ignore fields.
impl<'a, 'ph> Fields<'a, 'ph, Ignore> {
field_str!(
/// The ignore mask.
mask
);
field_int!(
/// Raw ignore flags.
raw_flags, unsafe { "flags" }
);
}
/// Notify fields.
impl<'a, 'ph> Fields<'a, 'ph, Notify> {
field_str!(
/// Comma-separated list of networks this notify entry applies to.
networks
);
field_str!(
/// The nick.
nick
);
field_int!(
/// Raw flags.
raw_flags, unsafe { "flags" }
);
field_time!(
/// Time when the user went online. [NYI]
on
);
field_time!(
/// Time when the user went offline. [NYI]
off
);
field_time!(
/// Time when the user was last seen. [NYI]
seen
);
}
/// Users fields.
impl<'a, 'ph> Fields<'a, 'ph, Users> {
field_str!(
/// Account name.
account
);
field_bool!(
/// Whether the user is away.
away
);
field_time!(
/// Time when the user last talked. [NYI]
lasttalk
);
field_str!(
/// User's nick.
nick
);
field_str!(
/// User's user and hostname (`user@host`).
host
);
field_str!(
/// User's prefix.
prefix
);
field_str!(
/// User's userdata.
userdata, unsafe { "realname" }
);
field_bool!(
/// Whether the user is selected in the UI.
selected
);
}
| |