hexchat_unsafe_plugin/
list.rs

1// This file is part of Hexchat Plugin API Bindings for Rust
2// Copyright (C) 2022 Soni L.
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as
6// published by the Free Software Foundation, either version 3 of the
7// License, or (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! List support module.
18
19use std::borrow::Cow;
20use std::ffi::CStr;
21
22use crate::Context;
23use crate::wrap_context;
24
25mod sealed {
26    pub trait Sealed {
27    }
28}
29
30/// A list. This trait is sealed.
31pub trait List: sealed::Sealed {
32    fn name(&self) -> Cow<'static, str>;
33}
34
35// List types
36
37/// A "channels" list.
38pub struct Contexts;
39
40/// A "dcc" list.
41pub struct Dcc;
42
43/// An "ignore" list.
44pub struct Ignore;
45
46/// A "notify" list.
47pub struct Notify;
48
49/// An "users" list.
50pub struct Users;
51
52/// Entries.
53pub struct Entries<'a, 'ph, T> {
54    pub(crate) context: &'a crate::PluginHandle<'ph>,
55    pub(crate) list: *mut crate::internals::HexchatList,
56    pub(crate) t: &'a T,
57}
58
59/// Fields.
60pub struct Fields<'a, 'ph, T> {
61    pub(crate) context: &'a crate::PluginHandle<'ph>,
62    pub(crate) list: *mut crate::internals::HexchatList,
63    pub(crate) _t: &'a T,
64}
65
66// impls
67
68impl sealed::Sealed for Contexts {}
69impl sealed::Sealed for Dcc {}
70impl sealed::Sealed for Ignore {}
71impl sealed::Sealed for Notify {}
72impl sealed::Sealed for Users {}
73
74impl List for Contexts {
75    fn name(&self) -> Cow<'static, str> { "channels".into() }
76}
77
78impl List for Dcc {
79    fn name(&self) -> Cow<'static, str> { "dcc".into() }
80}
81
82impl List for Ignore {
83    fn name(&self) -> Cow<'static, str> { "ignore".into() }
84}
85
86impl List for Notify {
87    fn name(&self) -> Cow<'static, str> { "notify".into() }
88}
89
90impl List for Users {
91    fn name(&self) -> Cow<'static, str> { "users".into() }
92}
93
94impl<'a, 'ph, T> Drop for Entries<'a, 'ph, T> {
95    fn drop(&mut self) {
96        let ph = &self.context;
97        unsafe {
98            ph_call!(hexchat_list_free(ph, self.list));
99        }
100    }
101}
102
103impl<'a, 'ph, T> Entries<'a, 'ph, T> {
104    /// Returns the next entry on the list, if any.
105    pub fn next<'b>(&'b mut self) -> Option<Fields<'b, 'ph, T>> where 'a: 'b {
106        let ph = &self.context;
107        if unsafe {
108            ph_call!(hexchat_list_next(ph, self.list))
109        } != 0 {
110            Some(Fields {
111                context: self.context,
112                list: self.list,
113                _t: self.t,
114            })
115        } else {
116            None
117        }
118    }
119}
120
121macro_rules! field {
122    ($(#[$m:meta])* $f:ident, unsafe {$n:expr}, $r:ty, $ph:ident, $c:ident, $($conv:tt)+) => {
123        $(#[$m])* pub fn $f(&self) -> $r {
124            let $ph = &self.context;
125            const NAME: &'static CStr = unsafe {
126                CStr::from_bytes_with_nul_unchecked(
127                    $n.as_bytes()
128                )
129            };
130            match unsafe {
131                ph_call!($c($ph, self.list, NAME.as_ptr()))
132            } {
133                $($conv)+
134            }
135        }
136    };
137    ($(#[$m:meta])* $f:ident, $r:ty, $ph:ident, $c:ident, $($conv:tt)+) => {
138        field!(
139            $(#[$m])* $f,
140            unsafe { ::std::concat!(::std::stringify!($f), "\0") },
141            $r,
142            $ph,
143            $c,
144            $($conv)+
145        );
146    };
147}
148
149macro_rules! field_int {
150    ($(#[$m:meta])* $f:ident, unsafe { $n:expr }) => {
151        field!($(#[$m])* $f, unsafe { $n }, i32, ph, hexchat_list_int, r => r as i32);
152    };
153    ($(#[$m:meta])* $f:ident) => {
154        field_int!($(#[$m])* $f, unsafe { ::std::concat!(::std::stringify!($f), "\0") });
155    };
156}
157
158macro_rules! field_bool {
159    ($(#[$m:meta])* $f:ident, unsafe { $n:expr }) => {
160        field!($(#[$m])* $f, unsafe { $n }, bool, ph, hexchat_list_int, r => r != 0);
161    };
162    ($(#[$m:meta])* $f:ident) => {
163        field_bool!($(#[$m])* $f, unsafe { ::std::concat!(::std::stringify!($f), "\0") });
164    };
165}
166
167macro_rules! field_str {
168    ($(#[$m:meta])* $f:ident, unsafe { $n:expr }) => {
169        field!(
170            $(#[$m])* $f, unsafe { $n }, Option<String>, ph, hexchat_list_str,
171            r if r.is_null() => {
172                None
173            },
174            r => Some(unsafe {
175                CStr::from_ptr(r)
176            }.to_owned().into_string().unwrap())
177        );
178    };
179    ($(#[$m:meta])* $f:ident) => {
180        field_str!($(#[$m])* $f, unsafe { ::std::concat!(::std::stringify!($f), "\0") });
181    };
182}
183
184
185macro_rules! field_time {
186    ($(#[$m:meta])* $f:ident, unsafe { $n:expr }) => {
187        field!($(#[$m])* $f, unsafe { $n }, libc::time_t, ph, hexchat_list_time, r => r as libc::time_t);
188    };
189    ($(#[$m:meta])* $f:ident) => {
190        field_time!($(#[$m])* $f, unsafe { ::std::concat!(::std::stringify!($f), "\0") });
191    };
192}
193
194/// Contexts fields.
195impl<'a, 'ph> Fields<'a, 'ph, Contexts> {
196    field_str!(
197        /// The context's name.
198        name, unsafe { "channel\0" }
199    );
200    field_str!(
201        /// The channel key.
202        channelkey
203    );
204    field_str!(
205        /// The server's channel modes. Requires HexChat 2.12.2+.
206        chanmodes
207    );
208    field_str!(
209        /// The server's channel types.
210        chantypes
211    );
212    field!(
213        /// The context.
214        context, Context<'ph>, ph, hexchat_list_str, r => unsafe {
215            wrap_context(ph, r as *const _)
216        }
217    );
218    field_int!(
219        /// Raw flags about this context.
220        raw_flags, unsafe { "flags\0" }
221    );
222    field_int!(
223        /// The server ID.
224        server_id, unsafe { "id\0" }
225    );
226    field_int!(
227        /// The latency to server in milliseconds.
228        lag
229    );
230    field_int!(
231        /// The maximum number of mode changes per line accepted by the server.
232        maxmodes
233    );
234    field_str!(
235        /// The network name.
236        network
237    );
238    field_str!(
239        /// The server's nick prefixes.
240        nickprefixes
241    );
242    field_str!(
243        /// The server's nick modes.
244        nickmodes
245    );
246    field_int!(
247        /// The current length of the send-queue, in bytes.
248        queue_len, unsafe { "queue\0" }
249    );
250    field_str!(
251        /// The server's name.
252        server
253    );
254    field_int!(
255        /// The context's raw type.
256        raw_type, unsafe { "type\0" }
257    );
258    field_int!(
259        /// The number of users in the channel.
260        users
261    );
262}
263
264/// Dcc fields.
265impl<'a, 'ph> Fields<'a, 'ph, Dcc> {
266    field_int!(
267        /// The raw 32-bit IPv4 address of the remote user.
268        address32
269    );
270    field_int!(
271        /// The transfer rate (speed), in bytes per second.
272        rate, unsafe { "cps\0" }
273    );
274    field_str!(
275        /// The destination file path.
276        destfile
277    );
278    field_str!(
279        /// The filename.
280        file
281    );
282    field_str!(
283        /// The remote user's nick.
284        nick
285    );
286    field_int!(
287        /// The listener's port number.
288        port
289    );
290    field_int!(
291        /// File position, LSB 32 bits.
292        raw_pos, unsafe { "pos\0" }
293    );
294    field_int!(
295        /// File position, MSB 32 bits.
296        raw_poshigh, unsafe { "poshigh\0" }
297    );
298    field_int!(
299        /// Resumed position, LSB 32 bits.
300        raw_resume, unsafe { "resume\0" }
301    );
302    field_int!(
303        /// Resumed position, MSB 32 bits.
304        raw_resumehigh, unsafe { "resumehigh\0" }
305    );
306    field_int!(
307        /// File size, LSB 32 bits.
308        raw_size, unsafe { "size\0" }
309    );
310    field_int!(
311        /// File size, MSB 32 bits.
312        raw_sizehigh, unsafe { "sizehigh\0" }
313    );
314    field_int!(
315        /// Raw DCC status.
316        raw_status, unsafe { "status\0" }
317    );
318    field_int!(
319        /// Raw type.
320        raw_type, unsafe { "type\0" }
321    );
322}
323
324/// Ignore fields.
325impl<'a, 'ph> Fields<'a, 'ph, Ignore> {
326    field_str!(
327        /// The ignore mask.
328        mask
329    );
330    field_int!(
331        /// Raw ignore flags.
332        raw_flags, unsafe { "flags\0" }
333    );
334}
335
336/// Notify fields.
337impl<'a, 'ph> Fields<'a, 'ph, Notify> {
338    field_str!(
339        /// Comma-separated list of networks this notify entry applies to.
340        networks
341    );
342    field_str!(
343        /// The nick.
344        nick
345    );
346    field_int!(
347        /// Raw flags.
348        raw_flags, unsafe { "flags\0" }
349    );
350    field_time!(
351        /// Time when the user went online.
352        raw_on, unsafe { "on\0" }
353    );
354    field_time!(
355        /// Time when the user went offline.
356        raw_off, unsafe { "off\0" }
357    );
358    field_time!(
359        /// Time when the user was last seen.
360        raw_seen, unsafe { "seen\0" }
361    );
362}
363
364/// Users fields.
365impl<'a, 'ph> Fields<'a, 'ph, Users> {
366    field_str!(
367        /// Account name.
368        account
369    );
370    field_bool!(
371        /// Whether the user is away.
372        away
373    );
374    field_time!(
375        /// Time when the user last talked.
376        raw_lasttalk, unsafe { "lasttalk\0" }
377    );
378    field_str!(
379        /// User's nick.
380        nick
381    );
382    field_str!(
383        /// User's user and hostname (`user@host`).
384        host
385    );
386    field_str!(
387        /// User's prefix.
388        prefix
389    );
390    field_str!(
391        /// User's userdata.
392        userdata, unsafe { "realname\0" }
393    );
394    field_bool!(
395        /// Whether the user is selected in the UI.
396        selected
397    );
398}