Skip to main content

recutils_rs/
db.rs

1use std::ffi::{CStr, c_char, c_void};
2use std::ptr;
3
4use crate::ffi::*;
5use crate::rset::{OwnedRset, Rset};
6use crate::{Error, cstring, ensure_init};
7
8unsafe extern "C" {
9    fn free(ptr: *mut c_void);
10}
11
12pub struct Db {
13    ptr: rec_db_t,
14}
15
16impl Db {
17    /// Allocate an empty database. Use [`Db::append_rset`] to add record sets
18    /// before serializing via [`Db::to_rec_string`].
19    pub fn new() -> Self {
20        ensure_init();
21        let ptr = unsafe { rec_db_new() };
22        assert!(!ptr.is_null(), "rec_db_new returned NULL");
23        Db { ptr }
24    }
25
26    pub fn parse_str(text: &str) -> Result<Self, Error> {
27        ensure_init();
28        let c_text = cstring(text, "rec source")?;
29        unsafe {
30            let parser = rec_parser_new_str(c_text.as_ptr(), c"input".as_ptr());
31            if parser.is_null() {
32                return Err(Error::new("rec_parser_new_str returned NULL"));
33            }
34            let mut db: rec_db_t = ptr::null_mut();
35            let ok = rec_parse_db(parser, &mut db);
36            rec_parser_destroy(parser);
37            if !ok || db.is_null() {
38                if !db.is_null() {
39                    rec_db_destroy(db);
40                }
41                return Err(Error::new("rec_parse_db failed"));
42            }
43            Ok(Db { ptr: db })
44        }
45    }
46
47    pub fn num_rsets(&self) -> usize {
48        unsafe { rec_db_size(self.ptr) }
49    }
50
51    pub fn rset_at(&mut self, idx: usize) -> Option<Rset<'_>> {
52        let p = unsafe { rec_db_get_rset(self.ptr, idx) };
53        (!p.is_null()).then(|| Rset::from_raw(p))
54    }
55
56    pub fn rset_by_type(&mut self, name: &str) -> Option<Rset<'_>> {
57        let c_name = cstring(name, "rset type").ok()?;
58        let p = unsafe { rec_db_get_rset_by_type(self.ptr, c_name.as_ptr()) };
59        (!p.is_null()).then(|| Rset::from_raw(p))
60    }
61
62    /// Append an [`OwnedRset`] to the end of the database. Ownership of the
63    /// rset transfers to librec; the rset will be freed when this `Db` is
64    /// dropped.
65    pub fn append_rset(&mut self, rset: OwnedRset) -> Result<(), Error> {
66        let raw = rset.into_raw();
67        let position = unsafe { rec_db_size(self.ptr) };
68        let ok = unsafe { rec_db_insert_rset(self.ptr, raw, position) };
69        if !ok {
70            unsafe { rec_rset_destroy(raw) };
71            return Err(Error::new("rec_db_insert_rset failed"));
72        }
73        Ok(())
74    }
75
76    pub fn to_rec_string(&self) -> Result<String, Error> {
77        unsafe {
78            let mut buf: *mut c_char = ptr::null_mut();
79            let mut size: usize = 0;
80            let writer = rec_writer_new_str(&mut buf, &mut size);
81            if writer.is_null() {
82                return Err(Error::new("rec_writer_new_str returned NULL"));
83            }
84            let ok = rec_write_db(writer, self.ptr);
85            rec_writer_destroy(writer);
86            if !ok || buf.is_null() {
87                if !buf.is_null() {
88                    free(buf as *mut c_void);
89                }
90                return Err(Error::new("rec_write_db failed"));
91            }
92            let s = CStr::from_ptr(buf).to_string_lossy().into_owned();
93            free(buf as *mut c_void);
94            Ok(s)
95        }
96    }
97}
98
99impl Default for Db {
100    fn default() -> Self {
101        Db::new()
102    }
103}
104
105impl Drop for Db {
106    fn drop(&mut self) {
107        unsafe { rec_db_destroy(self.ptr) }
108    }
109}