Skip to main content

qmap_rs/
lib.rs

1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4
5use std::ffi::{CStr, CString};
6use std::ptr;
7use libc::{c_char, c_void};
8
9mod bindings;
10pub use bindings::*;
11
12pub const QM_MISS: u32 = u32::MAX;
13
14/// Safe wrapper for a Qmap handle.
15pub struct Qmap {
16    hd: u32,
17}
18
19impl Qmap {
20    pub fn open(
21        filename: Option<&str>,
22        database: Option<&str>,
23        ktype: u32,
24        vtype: u32,
25        mask: u32,
26        flags: u32,
27    ) -> Option<Self> {
28        let c_filename = filename.map(|s| CString::new(s).unwrap());
29        let c_database = database.map(|s| CString::new(s).unwrap());
30
31        let hd = unsafe {
32            qmap_open(
33                c_filename.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
34                c_database.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
35                ktype,
36                vtype,
37                mask,
38                flags,
39            )
40        };
41
42        if hd == QM_MISS {
43            None
44        } else {
45            Some(Qmap { hd })
46        }
47    }
48
49    pub fn handle(&self) -> u32 {
50        self.hd
51    }
52
53    /// Creates a Qmap handle from a raw handle ID.
54    /// This should only be used for handles managed elsewhere (like in ndc C backend).
55    pub unsafe fn from_handle(hd: u32) -> Self {
56        Qmap { hd }
57    }
58
59    pub fn get(&self, key: *const c_void) -> *const c_void {
60        unsafe { qmap_get(self.hd, key) }
61    }
62
63    pub fn get_str(&self, key: &str) -> Option<&str> {
64        let c_key = CString::new(key).unwrap();
65        let val_ptr = unsafe { qmap_get(self.hd, c_key.as_ptr() as *const c_void) };
66        if val_ptr.is_null() {
67            None
68        } else {
69            unsafe {
70                CStr::from_ptr(val_ptr as *const c_char).to_str().ok()
71            }
72        }
73    }
74
75    pub fn put(&self, key: *const c_void, value: *const c_void) -> u32 {
76        unsafe { qmap_put(self.hd, key, value) }
77    }
78
79    pub fn put_str(&self, key: &str, value: &str) -> u32 {
80        let c_key = CString::new(key).unwrap();
81        let c_value = CString::new(value).unwrap();
82        unsafe {
83            qmap_put(
84                self.hd,
85                c_key.as_ptr() as *const c_void,
86                c_value.as_ptr() as *const c_void,
87            )
88        }
89    }
90
91    pub fn del(&self, key: *const c_void) {
92        unsafe { qmap_del(self.hd, key) }
93    }
94
95    pub fn drop(&self) {
96        unsafe { qmap_drop(self.hd) }
97    }
98
99    pub fn iter(&self, key: *const c_void, flags: u32) -> Cursor {
100        let cur_id = unsafe { qmap_iter(self.hd, key, flags) };
101        Cursor { cur_id }
102    }
103}
104
105impl Drop for Qmap {
106    fn drop(&mut self) {
107        unsafe { qmap_close(self.hd) }
108    }
109}
110
111pub struct Cursor {
112    cur_id: u32,
113}
114
115impl Cursor {
116    pub fn next(&mut self) -> Option<(*const c_void, *const c_void)> {
117        let mut key: *const c_void = ptr::null();
118        let mut value: *const c_void = ptr::null();
119        let res = unsafe { qmap_next(&mut key, &mut value, self.cur_id) };
120        if res == 1 {
121            Some((key, value))
122        } else {
123            None
124        }
125    }
126}
127
128impl Drop for Cursor {
129    fn drop(&mut self) {
130        unsafe { qmap_fin(self.cur_id) }
131    }
132}
133
134pub fn save() {
135    unsafe { qmap_save() }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    #[test]
143    fn test_qmap_open_in_memory() {
144        let q = Qmap::open(None, None, qmap_tbi_QM_STR, qmap_tbi_QM_STR, 0xFF, 0);
145        assert!(q.is_some());
146        let q = q.unwrap();
147        q.put_str("key1", "value1");
148        assert_eq!(q.get_str("key1"), Some("value1"));
149        assert_eq!(q.get_str("key2"), None);
150    }
151}