alpm_ll/
cb.rs

1use crate::{free, Alpm, AnyDownloadEvent, AnyEvent, AnyQuestion, FetchResult, LogLevel, Progress};
2use alpm_sys_ll::*;
3use std::cell::{RefCell, UnsafeCell};
4use std::ffi::{c_void, CStr};
5use std::mem::transmute;
6use std::os::raw::{c_char, c_int};
7use std::{fmt, panic, ptr};
8
9extern "C" {
10    fn vasprintf(str: *const *mut c_char, fmt: *const c_char, args: *mut __va_list_tag) -> c_int;
11}
12
13type Cb<T> = UnsafeCell<Option<Box<T>>>;
14
15#[derive(Default)]
16pub(crate) struct Callbacks {
17    pub(crate) log: Cb<dyn LogCbTrait>,
18    pub(crate) dl: Cb<dyn DlCbTrait>,
19    pub(crate) event: Cb<dyn EventCbTrait>,
20    pub(crate) progress: Cb<dyn ProgressCbTrait>,
21    pub(crate) question: Cb<dyn QuestionCbTrait>,
22    pub(crate) fetch: Cb<dyn FetchCbTrait>,
23}
24
25pub(crate) trait LogCbTrait {
26    fn call(&self, level: LogLevel, s: &str);
27    fn assert_unlocked(&self);
28}
29
30pub(crate) trait DlCbTrait {
31    fn call(&self, filename: &str, event: AnyDownloadEvent);
32    fn assert_unlocked(&self);
33}
34
35pub(crate) trait EventCbTrait {
36    fn call(&self, event: AnyEvent);
37    fn handle(&self) -> *mut alpm_handle_t;
38    fn assert_unlocked(&self);
39}
40
41pub(crate) trait ProgressCbTrait {
42    fn call(&self, progress: Progress, pkgname: &str, percent: i32, howmany: usize, current: usize);
43    fn assert_unlocked(&self);
44}
45
46pub(crate) trait QuestionCbTrait {
47    fn call(&self, question: AnyQuestion);
48    fn handle(&self) -> *mut alpm_handle_t;
49    fn assert_unlocked(&self);
50}
51
52pub(crate) trait FetchCbTrait {
53    fn call(&self, url: &str, filename: &str, force: bool) -> FetchResult;
54    fn assert_unlocked(&self);
55}
56
57struct LogCbImpl<T, F>(RefCell<(F, T)>);
58
59impl<T, F: FnMut(LogLevel, &str, &mut T)> LogCbTrait for LogCbImpl<T, F> {
60    fn call(&self, level: LogLevel, s: &str) {
61        let mut cb = self.0.borrow_mut();
62        let cb = &mut *cb;
63        (cb.0)(level, s, &mut cb.1)
64    }
65    fn assert_unlocked(&self) {
66        self.0.try_borrow_mut().expect("callback is in use");
67    }
68}
69
70struct DlCbImpl<T, F>(RefCell<(F, T)>);
71
72impl<T, F: FnMut(&str, AnyDownloadEvent, &mut T)> DlCbTrait for DlCbImpl<T, F> {
73    fn call(&self, s: &str, event: AnyDownloadEvent) {
74        let mut cb = self.0.borrow_mut();
75        let cb = &mut *cb;
76        (cb.0)(s, event, &mut cb.1)
77    }
78    fn assert_unlocked(&self) {
79        self.0.try_borrow_mut().expect("callback is in use");
80    }
81}
82
83struct EventCbImpl<T, F>(RefCell<(F, T)>, *mut alpm_handle_t);
84
85impl<T, F: FnMut(AnyEvent, &mut T)> EventCbTrait for EventCbImpl<T, F> {
86    fn call(&self, event: AnyEvent) {
87        let mut cb = self.0.borrow_mut();
88        let cb = &mut *cb;
89        (cb.0)(event, &mut cb.1)
90    }
91
92    fn assert_unlocked(&self) {
93        self.0.try_borrow_mut().expect("callback is in use");
94    }
95
96    fn handle(&self) -> *mut alpm_handle_t {
97        self.1
98    }
99}
100
101struct ProgressCbImpl<T, F>(RefCell<(F, T)>);
102
103impl<T, F: FnMut(Progress, &str, i32, usize, usize, &mut T)> ProgressCbTrait
104    for ProgressCbImpl<T, F>
105{
106    fn call(
107        &self,
108        progress: Progress,
109        pkgname: &str,
110        percent: i32,
111        howmany: usize,
112        current: usize,
113    ) {
114        let mut cb = self.0.borrow_mut();
115        let cb = &mut *cb;
116        (cb.0)(progress, pkgname, percent, howmany, current, &mut cb.1)
117    }
118    fn assert_unlocked(&self) {
119        self.0.try_borrow_mut().expect("callback is in use");
120    }
121}
122
123struct QuestionCbImpl<T, F>(RefCell<(F, T)>, *mut alpm_handle_t);
124
125impl<T, F: FnMut(AnyQuestion, &mut T)> QuestionCbTrait for QuestionCbImpl<T, F> {
126    fn call(&self, question: AnyQuestion) {
127        let mut cb = self.0.borrow_mut();
128        let cb = &mut *cb;
129        (cb.0)(question, &mut cb.1)
130    }
131    fn assert_unlocked(&self) {
132        self.0.try_borrow_mut().expect("callback is in use");
133    }
134
135    fn handle(&self) -> *mut alpm_handle_t {
136        self.1
137    }
138}
139
140struct FetchCbImpl<T, F>(RefCell<(F, T)>);
141
142impl<T, F: FnMut(&str, &str, bool, &mut T) -> FetchResult> FetchCbTrait for FetchCbImpl<T, F> {
143    fn call(&self, url: &str, filename: &str, force: bool) -> FetchResult {
144        let mut cb = self.0.borrow_mut();
145        let cb = &mut *cb;
146        (cb.0)(url, filename, force, &mut cb.1)
147    }
148
149    fn assert_unlocked(&self) {
150        self.0.try_borrow_mut().expect("callback is in use");
151    }
152}
153
154pub struct RawLogCb {
155    pub(crate) raw: alpm_cb_log,
156    pub(crate) ctx: *mut c_void,
157    pub(crate) cb: Option<Box<dyn LogCbTrait>>,
158}
159
160impl fmt::Debug for RawLogCb {
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        f.write_str("RawLogCb")
163    }
164}
165
166pub struct RawDlCb {
167    pub(crate) raw: alpm_cb_download,
168    pub(crate) ctx: *mut c_void,
169    pub(crate) cb: Option<Box<dyn DlCbTrait>>,
170}
171
172impl fmt::Debug for RawDlCb {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        f.write_str("RawDlCb")
175    }
176}
177
178pub struct RawEventCb {
179    pub(crate) raw: alpm_cb_event,
180    pub(crate) ctx: *mut c_void,
181    pub(crate) cb: Option<Box<dyn EventCbTrait>>,
182}
183
184impl fmt::Debug for RawEventCb {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        f.write_str("RawEventCb")
187    }
188}
189
190pub struct RawProgressCb {
191    pub(crate) raw: alpm_cb_progress,
192    pub(crate) ctx: *mut c_void,
193    pub(crate) cb: Option<Box<dyn ProgressCbTrait>>,
194}
195
196impl fmt::Debug for RawProgressCb {
197    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198        f.write_str("RawProgressCb")
199    }
200}
201
202pub struct RawQuestionCb {
203    pub(crate) raw: alpm_cb_question,
204    pub(crate) ctx: *mut c_void,
205    pub(crate) cb: Option<Box<dyn QuestionCbTrait>>,
206}
207
208impl fmt::Debug for RawQuestionCb {
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        f.write_str("RawQuestionCb")
211    }
212}
213
214pub struct RawFetchCb {
215    pub(crate) raw: alpm_cb_fetch,
216    pub(crate) ctx: *mut c_void,
217    pub(crate) cb: Option<Box<dyn FetchCbTrait>>,
218}
219
220impl fmt::Debug for RawFetchCb {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        f.write_str("RawFetchCb")
223    }
224}
225
226impl Alpm {
227    pub fn set_log_cb<T: 'static, F: FnMut(LogLevel, &str, &mut T) + 'static>(
228        &self,
229        data: T,
230        f: F,
231    ) {
232        let c = unsafe { &mut *self.cbs.log.get() };
233        if let Some(cb) = c.as_ref() {
234            cb.assert_unlocked()
235        }
236        let ctx = LogCbImpl(RefCell::new((f, data)));
237        let ctx = Box::new(ctx);
238        let cb = logcb::<LogCbImpl<T, F>>;
239        unsafe { self.lib.alpm_option_set_logcb(self.as_ptr(), Some(cb), &*ctx as *const _ as *mut _) };
240        c.replace(ctx);
241    }
242
243    pub fn set_dl_cb<T: 'static, F: FnMut(&str, AnyDownloadEvent, &mut T) + 'static>(
244        &self,
245        data: T,
246        f: F,
247    ) {
248        let c = unsafe { &mut *self.cbs.dl.get() };
249        if let Some(cb) = c.as_ref() {
250            cb.assert_unlocked()
251        }
252
253        if let Some(cb) = c.as_ref() {
254            cb.assert_unlocked()
255        }
256        let ctx = DlCbImpl(RefCell::new((f, data)));
257        let ctx = Box::new(ctx);
258        let cb = dlcb::<DlCbImpl<T, F>>;
259        unsafe { self.lib.alpm_option_set_dlcb(self.as_ptr(), Some(cb), &*ctx as *const _ as *mut _) };
260        c.replace(ctx);
261    }
262
263    pub fn set_event_cb<T: 'static, F: FnMut(AnyEvent, &mut T) + 'static>(&self, data: T, f: F) {
264        let c = unsafe { &mut *self.cbs.event.get() };
265        if let Some(cb) = c.as_ref() {
266            cb.assert_unlocked()
267        }
268        let ctx = EventCbImpl(RefCell::new((f, data)), self.as_ptr());
269        let ctx = Box::new(ctx);
270        let cb = eventcb::<EventCbImpl<T, F>>;
271        unsafe { self.lib.alpm_option_set_eventcb(self.as_ptr(), Some(cb), &*ctx as *const _ as *mut _) };
272        c.replace(ctx);
273    }
274
275    pub fn set_progress_cb<
276        T: 'static,
277        F: FnMut(Progress, &str, i32, usize, usize, &mut T) + 'static,
278    >(
279        &self,
280        data: T,
281        f: F,
282    ) {
283        let c = unsafe { &mut *self.cbs.progress.get() };
284        if let Some(cb) = c.as_ref() {
285            cb.assert_unlocked()
286        }
287        let ctx = ProgressCbImpl(RefCell::new((f, data)));
288        let ctx = Box::new(ctx);
289        let cb = progresscb::<ProgressCbImpl<T, F>>;
290        unsafe { self.lib.alpm_option_set_progresscb(self.as_ptr(), Some(cb), &*ctx as *const _ as *mut _) };
291        c.replace(ctx);
292    }
293
294    pub fn set_question_cb<T: 'static, F: FnMut(AnyQuestion, &mut T) + 'static>(
295        &self,
296        data: T,
297        f: F,
298    ) {
299        let c = unsafe { &mut *self.cbs.question.get() };
300        if let Some(cb) = c.as_ref() {
301            cb.assert_unlocked()
302        }
303        let ctx = QuestionCbImpl(RefCell::new((f, data)), self.as_ptr());
304        let ctx = Box::new(ctx);
305        let cb = questioncb::<QuestionCbImpl<T, F>>;
306        unsafe { self.lib.alpm_option_set_questioncb(self.as_ptr(), Some(cb), &*ctx as *const _ as *mut _) };
307        c.replace(ctx);
308    }
309
310    pub fn set_fetch_cb<T: 'static, F: FnMut(&str, &str, bool, &mut T) -> FetchResult + 'static>(
311        &self,
312        data: T,
313        f: F,
314    ) {
315        let c = unsafe { &mut *self.cbs.fetch.get() };
316        if let Some(cb) = c.as_ref() {
317            cb.assert_unlocked()
318        }
319        let ctx = FetchCbImpl(RefCell::new((f, data)));
320        let ctx = Box::new(ctx);
321        let cb = fetchcb::<FetchCbImpl<T, F>>;
322        unsafe { self.lib.alpm_option_set_fetchcb(self.as_ptr(), Some(cb), &*ctx as *const _ as *mut _) };
323        c.replace(ctx);
324    }
325
326    pub fn take_raw_log_cb(&self) -> RawLogCb {
327        let c = unsafe { &mut *self.cbs.log.get() };
328        if let Some(cb) = c.as_ref() {
329            cb.assert_unlocked()
330        }
331
332        let cb = RawLogCb {
333            ctx: unsafe { self.lib.alpm_option_get_logcb_ctx(self.as_ptr()) },
334            raw: unsafe { self.lib.alpm_option_get_logcb(self.as_ptr()) },
335            cb: c.take(),
336        };
337        unsafe { self.lib.alpm_option_set_logcb(self.as_ptr(), None, ptr::null_mut()) };
338        cb
339    }
340
341    pub fn set_raw_log_cb(&self, cb: RawLogCb) {
342        let c = unsafe { &mut *self.cbs.log.get() };
343        if let Some(cb) = c.as_ref() {
344            cb.assert_unlocked()
345        }
346        unsafe { self.lib.alpm_option_set_logcb(self.as_ptr(), cb.raw, cb.ctx) };
347        *c = cb.cb
348    }
349
350    pub fn take_raw_dl_cb(&self) -> RawDlCb {
351        let c = unsafe { &mut *self.cbs.dl.get() };
352        if let Some(cb) = c.as_ref() {
353            cb.assert_unlocked()
354        }
355        let cb = RawDlCb {
356            ctx: unsafe { self.lib.alpm_option_get_dlcb_ctx(self.as_ptr()) },
357            raw: unsafe { self.lib.alpm_option_get_dlcb(self.as_ptr()) },
358            cb: c.take(),
359        };
360        unsafe { self.lib.alpm_option_set_dlcb(self.as_ptr(), None, ptr::null_mut()) };
361        cb
362    }
363
364    pub fn set_raw_dl_cb(&self, cb: RawDlCb) {
365        let c = unsafe { &mut *self.cbs.dl.get() };
366        if let Some(cb) = c.as_ref() {
367            cb.assert_unlocked()
368        }
369        unsafe { self.lib.alpm_option_set_dlcb(self.as_ptr(), cb.raw, cb.ctx) };
370        *c = cb.cb
371    }
372
373    pub fn take_raw_event_cb(&self) -> RawEventCb {
374        let c = unsafe { &mut *self.cbs.event.get() };
375        if let Some(cb) = c.as_ref() {
376            cb.assert_unlocked()
377        }
378        let cb = RawEventCb {
379            ctx: unsafe { self.lib.alpm_option_get_eventcb_ctx(self.as_ptr()) },
380            raw: unsafe { self.lib.alpm_option_get_eventcb(self.as_ptr()) },
381            cb: c.take(),
382        };
383        unsafe { self.lib.alpm_option_set_eventcb(self.as_ptr(), None, ptr::null_mut()) };
384        cb
385    }
386
387    pub fn set_raw_event_cb(&self, cb: RawEventCb) {
388        let c = unsafe { &mut *self.cbs.event.get() };
389        if let Some(cb) = c.as_ref() {
390            cb.assert_unlocked()
391        }
392
393        unsafe { self.lib.alpm_option_set_eventcb(self.as_ptr(), cb.raw, cb.ctx) };
394        *c = cb.cb
395    }
396
397    pub fn take_raw_progress_cb(&self) -> RawProgressCb {
398        let c = unsafe { &mut *self.cbs.progress.get() };
399        if let Some(cb) = c.as_ref() {
400            cb.assert_unlocked()
401        }
402
403        let cb = RawProgressCb {
404            ctx: unsafe { self.lib.alpm_option_get_progresscb_ctx(self.as_ptr()) },
405            raw: unsafe { self.lib.alpm_option_get_progresscb(self.as_ptr()) },
406            cb: c.take(),
407        };
408        unsafe { self.lib.alpm_option_set_progresscb(self.as_ptr(), None, ptr::null_mut()) };
409        cb
410    }
411
412    pub fn set_raw_progress_cb(&self, cb: RawProgressCb) {
413        let c = unsafe { &mut *self.cbs.progress.get() };
414        if let Some(cb) = c.as_ref() {
415            cb.assert_unlocked()
416        }
417
418        unsafe { self.lib.alpm_option_set_progresscb(self.as_ptr(), cb.raw, cb.ctx) };
419        *c = cb.cb;
420    }
421
422    pub fn take_raw_question_cb(&self) -> RawQuestionCb {
423        let c = unsafe { &mut *self.cbs.question.get() };
424        if let Some(cb) = c.as_ref() {
425            cb.assert_unlocked()
426        }
427
428        let cb = RawQuestionCb {
429            ctx: unsafe { self.lib.alpm_option_get_questioncb_ctx(self.as_ptr()) },
430            raw: unsafe { self.lib.alpm_option_get_questioncb(self.as_ptr()) },
431            cb: c.take(),
432        };
433        unsafe { self.lib.alpm_option_set_questioncb(self.as_ptr(), None, ptr::null_mut()) };
434        cb
435    }
436
437    pub fn set_raw_question_cb(&self, cb: RawQuestionCb) {
438        let c = unsafe { &mut *self.cbs.question.get() };
439        if let Some(cb) = c.as_ref() {
440            cb.assert_unlocked()
441        }
442
443        unsafe { self.lib.alpm_option_set_questioncb(self.as_ptr(), cb.raw, cb.ctx) };
444        *c = cb.cb;
445    }
446
447    pub fn take_raw_fetch_cb(&self) -> RawFetchCb {
448        let c = unsafe { &mut *self.cbs.fetch.get() };
449        if let Some(cb) = c.as_ref() {
450            cb.assert_unlocked()
451        }
452
453        let cb = RawFetchCb {
454            ctx: unsafe { self.lib.alpm_option_get_fetchcb_ctx(self.as_ptr()) },
455            raw: unsafe { self.lib.alpm_option_get_fetchcb(self.as_ptr()) },
456            cb: c.take(),
457        };
458
459        unsafe { self.lib.alpm_option_set_fetchcb(self.as_ptr(), None, ptr::null_mut()) };
460        cb
461    }
462
463    pub fn set_raw_fetch_cb(&self, cb: RawFetchCb) {
464        let c = unsafe { &mut *self.cbs.fetch.get() };
465        if let Some(cb) = c.as_ref() {
466            cb.assert_unlocked()
467        }
468
469        unsafe { self.lib.alpm_option_set_fetchcb(self.as_ptr(), cb.raw, cb.ctx) };
470        *c = cb.cb;
471    }
472}
473
474extern "C" fn logcb<C: LogCbTrait>(
475    ctx: *mut c_void,
476    level: alpm_loglevel_t,
477    fmt: *const c_char,
478    args: *mut __va_list_tag,
479) {
480    let buff = ptr::null_mut();
481    let n = unsafe { vasprintf(&buff, fmt, args) };
482    if n != -1 {
483        let _ = panic::catch_unwind(|| {
484            let s = unsafe { CStr::from_ptr(buff) };
485            let level = LogLevel::from_bits(level).unwrap();
486            let cb = unsafe { &*(ctx as *const C) };
487            cb.call(level, &s.to_string_lossy());
488        });
489
490        unsafe { free(buff as *mut c_void) };
491    }
492}
493
494extern "C" fn dlcb<C: DlCbTrait>(
495    ctx: *mut c_void,
496    filename: *const c_char,
497    event: alpm_download_event_type_t,
498    data: *mut c_void,
499) {
500    let _ = panic::catch_unwind(|| {
501        let filename = unsafe { CStr::from_ptr(filename) };
502        let filename = filename.to_str().unwrap();
503        let event = unsafe { AnyDownloadEvent::new(event, data) };
504        let cb = unsafe { &*(ctx as *const C) };
505        cb.call(filename, event);
506    });
507}
508
509extern "C" fn fetchcb<C: FetchCbTrait>(
510    ctx: *mut c_void,
511    url: *const c_char,
512    localpath: *const c_char,
513    force: c_int,
514) -> c_int {
515    let ret = panic::catch_unwind(|| {
516        let url = unsafe { CStr::from_ptr(url).to_str().unwrap() };
517        let localpath = unsafe { CStr::from_ptr(localpath).to_str().unwrap() };
518        let cb = unsafe { &*(ctx as *const C) };
519        let ret = cb.call(url, localpath, force != 0);
520
521        match ret {
522            FetchResult::Ok => 0,
523            FetchResult::Err => -1,
524            FetchResult::FileExists => 1,
525        }
526    });
527
528    ret.unwrap_or(-1)
529}
530
531extern "C" fn eventcb<C: EventCbTrait>(ctx: *mut c_void, event: *mut alpm_event_t) {
532    let _ = panic::catch_unwind(|| {
533        let cb = unsafe { &*(ctx as *const C) };
534
535        let event = unsafe { AnyEvent::new(cb.handle(), event) };
536        cb.call(event);
537    });
538}
539
540extern "C" fn questioncb<C: QuestionCbTrait>(ctx: *mut c_void, question: *mut alpm_question_t) {
541    let _ = panic::catch_unwind(|| {
542        let cb = unsafe { &*(ctx as *const C) };
543        let question = unsafe { AnyQuestion::new(cb.handle(), question) };
544        cb.call(question);
545    });
546}
547
548extern "C" fn progresscb<C: ProgressCbTrait>(
549    ctx: *mut c_void,
550    progress: alpm_progress_t,
551    pkgname: *const c_char,
552    percent: c_int,
553    howmany: usize,
554    current: usize,
555) {
556    let _ = panic::catch_unwind(|| {
557        let pkgname = unsafe { CStr::from_ptr(pkgname) };
558        let pkgname = pkgname.to_str().unwrap();
559        let progress = unsafe { transmute::<alpm_progress_t, Progress>(progress) };
560        let cb = unsafe { &*(ctx as *const C) };
561        cb.call(progress, pkgname, percent as i32, howmany, current);
562    });
563}
564
565#[cfg(test)]
566mod tests {
567    use super::*;
568    use crate::{
569        log_action, version, AnyDownloadEvent, AnyEvent, AnyQuestion, Capabilities, DownloadEvent,
570        Event, FetchResult, Progress, Question, SigLevel,
571    };
572    use std::cell::Cell;
573    use std::rc::Rc;
574
575    fn eventcb(event: AnyEvent, _: &mut ()) {
576        match event.event() {
577            Event::DatabaseMissing(x) => println!("missing database: {}", x.dbname()),
578            _ => println!("event: {:?}", event),
579        }
580    }
581
582    fn fetchcb(_url: &str, _path: &str, _force: bool, _: &mut ()) -> FetchResult {
583        FetchResult::Ok
584    }
585
586    fn questioncb(question: AnyQuestion, _: &mut ()) {
587        println!("question {:?}", question);
588        match question.question() {
589            Question::Conflict(x) => {
590                let c = x.conflict();
591                println!("CONFLICT BETWEEN {} AND {}", c.package1(), c.package2(),);
592                println!("conflict: {}", c.reason());
593            }
594            _ => (),
595        }
596    }
597
598    fn downloadcb(filename: &str, download: AnyDownloadEvent, _: &mut ()) {
599        match download.event() {
600            DownloadEvent::Init(init) => {
601                println!("init: file={} optional={}", filename, init.optional)
602            }
603            DownloadEvent::Completed(comp) => println!(
604                "complete: file={} total={} result={:?}",
605                filename, comp.total, comp.result
606            ),
607            _ => (),
608        }
609    }
610
611    fn progresscb(
612        progress: Progress,
613        pkgname: &str,
614        percent: i32,
615        howmany: usize,
616        current: usize,
617        _: &mut (),
618    ) {
619        println!(
620            "progress {:?}, {} {} {} {}",
621            progress, pkgname, percent, howmany, current
622        );
623    }
624
625    #[test]
626    fn test_capabilities() {
627        let _caps = Capabilities::new();
628    }
629
630    #[test]
631    fn test_init() {
632        let _handle = Alpm::new("/", "tests/db").unwrap();
633    }
634
635    #[test]
636    fn test_version() {
637        assert!(!version().is_empty());
638    }
639
640    #[test]
641    fn test_cb() {
642        let mut handle = Alpm::new("/", "tests/db").unwrap();
643
644        handle.set_use_syslog(true);
645        handle.set_logfile("tests/log").unwrap();
646        handle.set_log_cb(0, |_, msg, data| {
647            print!("log {} {}", data, msg);
648            *data += 1;
649        });
650        handle.set_event_cb((), eventcb);
651        handle.set_fetch_cb((), fetchcb);
652        handle.set_question_cb((), questioncb);
653        handle.set_dl_cb((), downloadcb);
654        handle.set_progress_cb((), progresscb);
655
656        log_action!(handle, "me", "look i am logging an action {}", ":D").unwrap();
657        handle
658            .log_action("me", "look i am logging an action 2")
659            .unwrap();
660        handle
661            .log_action("me", "look i am logging an action 2")
662            .unwrap();
663        handle
664            .log_action("me", "look i am logging an action 2")
665            .unwrap();
666        handle
667            .log_action("me", "look i am logging an action 2")
668            .unwrap();
669        handle
670            .log_action("me", "look i am logging an action 2")
671            .unwrap();
672
673        let db = handle.register_syncdb_mut("core", SigLevel::NONE).unwrap();
674        db.add_server("https://ftp.rnl.tecnico.ulisboa.pt/pub/archlinux/core/os/x86_64")
675            .unwrap();
676        db.pkg("filesystem").unwrap();
677    }
678
679    #[test]
680    fn test_cb_data() {
681        let handle = Alpm::new("/", "tests/db").unwrap();
682
683        let data = Rc::new(Cell::new(0));
684
685        handle.set_log_cb(data.clone(), |_, _, data| data.set(7));
686        handle.register_syncdb("core", SigLevel::NONE).unwrap();
687
688        assert_eq!(data.get(), 7);
689    }
690
691    #[test]
692    fn test_cb_refcell1() {
693        let handle = Alpm::new("/", "tests/db").unwrap();
694        let handle = Rc::new(handle);
695
696        handle.set_log_cb(Rc::downgrade(&handle), |_, msg, data| {
697            let handle = data.upgrade().unwrap();
698            println!("{} {:?}", msg, handle);
699            handle.take_raw_log_cb();
700        });
701        handle.register_syncdb("core", SigLevel::NONE).unwrap();
702    }
703
704    #[test]
705    fn test_cb_refcell2() {
706        let handle = Alpm::new("/", "tests/db").unwrap();
707        let handle = Rc::new(handle);
708
709        handle.set_log_cb(Rc::downgrade(&handle), |_, msg, data| {
710            let handle = data.upgrade().unwrap();
711            println!("{} {:?}", msg, handle);
712            handle.set_log_cb((), |_, _, _| {});
713        });
714        handle.register_syncdb("core", SigLevel::NONE).unwrap();
715    }
716
717    #[ignore]
718    #[test]
719    fn test_cb_refcell_mut() {
720        let handle = Alpm::new("/", "tests/db").unwrap();
721        let handle = Rc::new(RefCell::new(handle));
722        let borrow = handle.borrow();
723        let db = borrow.register_syncdb("core", SigLevel::NONE).unwrap();
724
725        handle
726            .borrow()
727            .set_log_cb(Rc::clone(&handle), |_, msg, data| {
728                let handle = data;
729                println!("{} {:?}", msg, handle);
730                handle.borrow_mut().unregister_all_syncdbs().unwrap();
731                println!("Done");
732            });
733
734        println!("{:?}", db.pkg("linux"));
735        assert_eq!(handle.borrow().syncdbs().len(), 1);
736    }
737
738    #[test]
739    fn test_cb_drop() {
740        let handle = Alpm::new("/", "tests/db").unwrap();
741        let mut val = Rc::new(42);
742        handle.set_log_cb(Rc::clone(&val), |_, _, _| ());
743        assert!(Rc::get_mut(&mut val).is_none());
744        let cb = handle.take_raw_log_cb();
745        assert!(Rc::get_mut(&mut val).is_none());
746        drop(cb);
747        Rc::get_mut(&mut val).unwrap();
748        drop(handle);
749    }
750}