1use 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
30pub trait List: sealed::Sealed {
32 fn name(&self) -> Cow<'static, str>;
33}
34
35pub struct Contexts;
39
40pub struct Dcc;
42
43pub struct Ignore;
45
46pub struct Notify;
48
49pub struct Users;
51
52pub 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
59pub 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
66impl 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 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
194impl<'a, 'ph> Fields<'a, 'ph, Contexts> {
196 field_str!(
197 name, unsafe { "channel\0" }
199 );
200 field_str!(
201 channelkey
203 );
204 field_str!(
205 chanmodes
207 );
208 field_str!(
209 chantypes
211 );
212 field!(
213 context, Context<'ph>, ph, hexchat_list_str, r => unsafe {
215 wrap_context(ph, r as *const _)
216 }
217 );
218 field_int!(
219 raw_flags, unsafe { "flags\0" }
221 );
222 field_int!(
223 server_id, unsafe { "id\0" }
225 );
226 field_int!(
227 lag
229 );
230 field_int!(
231 maxmodes
233 );
234 field_str!(
235 network
237 );
238 field_str!(
239 nickprefixes
241 );
242 field_str!(
243 nickmodes
245 );
246 field_int!(
247 queue_len, unsafe { "queue\0" }
249 );
250 field_str!(
251 server
253 );
254 field_int!(
255 raw_type, unsafe { "type\0" }
257 );
258 field_int!(
259 users
261 );
262}
263
264impl<'a, 'ph> Fields<'a, 'ph, Dcc> {
266 field_int!(
267 address32
269 );
270 field_int!(
271 rate, unsafe { "cps\0" }
273 );
274 field_str!(
275 destfile
277 );
278 field_str!(
279 file
281 );
282 field_str!(
283 nick
285 );
286 field_int!(
287 port
289 );
290 field_int!(
291 raw_pos, unsafe { "pos\0" }
293 );
294 field_int!(
295 raw_poshigh, unsafe { "poshigh\0" }
297 );
298 field_int!(
299 raw_resume, unsafe { "resume\0" }
301 );
302 field_int!(
303 raw_resumehigh, unsafe { "resumehigh\0" }
305 );
306 field_int!(
307 raw_size, unsafe { "size\0" }
309 );
310 field_int!(
311 raw_sizehigh, unsafe { "sizehigh\0" }
313 );
314 field_int!(
315 raw_status, unsafe { "status\0" }
317 );
318 field_int!(
319 raw_type, unsafe { "type\0" }
321 );
322}
323
324impl<'a, 'ph> Fields<'a, 'ph, Ignore> {
326 field_str!(
327 mask
329 );
330 field_int!(
331 raw_flags, unsafe { "flags\0" }
333 );
334}
335
336impl<'a, 'ph> Fields<'a, 'ph, Notify> {
338 field_str!(
339 networks
341 );
342 field_str!(
343 nick
345 );
346 field_int!(
347 raw_flags, unsafe { "flags\0" }
349 );
350 field_time!(
351 raw_on, unsafe { "on\0" }
353 );
354 field_time!(
355 raw_off, unsafe { "off\0" }
357 );
358 field_time!(
359 raw_seen, unsafe { "seen\0" }
361 );
362}
363
364impl<'a, 'ph> Fields<'a, 'ph, Users> {
366 field_str!(
367 account
369 );
370 field_bool!(
371 away
373 );
374 field_time!(
375 raw_lasttalk, unsafe { "lasttalk\0" }
377 );
378 field_str!(
379 nick
381 );
382 field_str!(
383 host
385 );
386 field_str!(
387 prefix
389 );
390 field_str!(
391 userdata, unsafe { "realname\0" }
393 );
394 field_bool!(
395 selected
397 );
398}