Skip to main content

recutils_rs/
record.rs

1use std::ffi::{CStr, c_void};
2use std::marker::PhantomData;
3use std::ptr;
4
5use crate::ffi::*;
6use crate::{Error, cstring, ensure_init};
7
8/// A freshly-built record. Owns its `rec_record_t` until passed to
9/// [`crate::Rset::append_record`], at which point ownership is transferred to
10/// the containing record set.
11pub struct Record {
12    ptr: rec_record_t,
13}
14
15impl Record {
16    pub fn new() -> Self {
17        ensure_init();
18        let ptr = unsafe { rec_record_new() };
19        assert!(!ptr.is_null(), "rec_record_new returned NULL");
20        Record { ptr }
21    }
22
23    pub fn append_field(&mut self, name: &str, value: &str) -> Result<(), Error> {
24        let cn = cstring(name, "field name")?;
25        let cv = cstring(value, "field value")?;
26        unsafe {
27            let field = rec_field_new(cn.as_ptr(), cv.as_ptr());
28            if field.is_null() {
29                return Err(Error::new("rec_field_new returned NULL"));
30            }
31            let mset = rec_record_mset(self.ptr);
32            let elem = rec_mset_append(
33                mset,
34                MSET_FIELD as rec_mset_type_t,
35                field as *mut c_void,
36                MSET_FIELD as rec_mset_type_t,
37            );
38            if elem.is_null() {
39                rec_field_destroy(field);
40                return Err(Error::new("rec_mset_append (field) failed"));
41            }
42        }
43        Ok(())
44    }
45
46    pub(crate) fn into_raw(self) -> rec_record_t {
47        let ptr = self.ptr;
48        std::mem::forget(self);
49        ptr
50    }
51}
52
53impl Default for Record {
54    fn default() -> Self {
55        Record::new()
56    }
57}
58
59impl Drop for Record {
60    fn drop(&mut self) {
61        unsafe { rec_record_destroy(self.ptr) }
62    }
63}
64
65pub struct RecordRef<'a> {
66    ptr: rec_record_t,
67    _marker: PhantomData<&'a ()>,
68}
69
70impl<'a> RecordRef<'a> {
71    pub(crate) fn from_raw(ptr: rec_record_t) -> Self {
72        RecordRef { ptr, _marker: PhantomData }
73    }
74
75    pub fn fields(&self) -> Fields<'_> {
76        let iter = unsafe { rec_mset_iterator(rec_record_mset(self.ptr)) };
77        Fields { iter, done: false, _marker: PhantomData }
78    }
79
80    pub fn as_ptr(&self) -> rec_record_t {
81        self.ptr
82    }
83
84    /// Set the value of the first field with the given `name`. Returns `true`
85    /// if such a field was found and updated, `false` otherwise.
86    pub fn set_field(&mut self, name: &str, value: &str) -> Result<bool, Error> {
87        let cn = cstring(name, "field name")?;
88        let cv = cstring(value, "field value")?;
89        unsafe {
90            let field = rec_record_get_field_by_name(self.ptr, cn.as_ptr(), 0);
91            if field.is_null() {
92                return Ok(false);
93            }
94            if !rec_field_set_value(field, cv.as_ptr()) {
95                return Err(Error::new("rec_field_set_value failed"));
96            }
97        }
98        Ok(true)
99    }
100}
101
102pub struct Fields<'a> {
103    iter: rec_mset_iterator_t,
104    done: bool,
105    _marker: PhantomData<&'a RecordRef<'a>>,
106}
107
108impl<'a> Iterator for Fields<'a> {
109    type Item = FieldRef<'a>;
110
111    fn next(&mut self) -> Option<Self::Item> {
112        if self.done {
113            return None;
114        }
115        let mut data: *const c_void = ptr::null();
116        let advanced = unsafe {
117            rec_mset_iterator_next(
118                &mut self.iter,
119                MSET_FIELD as rec_mset_type_t,
120                &mut data,
121                ptr::null_mut(),
122            )
123        };
124        if !advanced {
125            self.done = true;
126            return None;
127        }
128        Some(FieldRef { ptr: data as rec_field_t, _marker: PhantomData })
129    }
130}
131
132impl<'a> Drop for Fields<'a> {
133    fn drop(&mut self) {
134        unsafe { rec_mset_iterator_free(&mut self.iter) }
135    }
136}
137
138pub struct FieldRef<'a> {
139    ptr: rec_field_t,
140    _marker: PhantomData<&'a ()>,
141}
142
143impl<'a> FieldRef<'a> {
144    pub fn name(&self) -> String {
145        unsafe { CStr::from_ptr(rec_field_name(self.ptr)).to_string_lossy().into_owned() }
146    }
147
148    pub fn value(&self) -> String {
149        unsafe { CStr::from_ptr(rec_field_value(self.ptr)).to_string_lossy().into_owned() }
150    }
151}