diff options
author | SoniEx2 <endermoneymod@gmail.com> | 2022-04-26 00:06:29 -0300 |
---|---|---|
committer | SoniEx2 <endermoneymod@gmail.com> | 2022-04-26 00:06:29 -0300 |
commit | 25fde2f4231ae5c651d2e7a3a4d17868dd0d9c1a (patch) | |
tree | 42ebe008ef1f08e630aec329e539ee89f94810b8 /src/list.rs | |
parent | f9f854aa51e384358d935740bd64cf4f6d3057c5 (diff) |
Add support for lists
Diffstat (limited to 'src/list.rs')
-rw-r--r-- | src/list.rs | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/src/list.rs b/src/list.rs new file mode 100644 index 0000000..257ef95 --- /dev/null +++ b/src/list.rs @@ -0,0 +1,413 @@ +// 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 <https://www.gnu.org/licenses/>. + +//! 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::ValidContext<'a, 'ph>, + pub(crate) list: *mut crate::internals::HexchatList, + pub(crate) t: &'a T, + pub(crate) valid: Rc<Cell<usize>>, +} + +/// Fields. +pub struct Fields<'a, 'ph, T> { + context: &'a crate::ValidContext<'a, 'ph>, + list: *mut crate::internals::HexchatList, + _t: &'a T, + id: usize, + valid: Rc<Cell<usize>>, +} + +// 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.ph; + 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<Self::Item> { + let ph = &self.context.ph; + 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.ph; + 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<String>, 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 + ); +} |