1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::{handle::Handle, ColumnFamily, Error};
use libc::{c_char, c_void};
use std::ffi::{CStr, CString};

pub trait GetProperty {
    /// Retrieves a RocksDB property by name.
    ///
    /// For a full list of properties, see
    /// https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L428-L634
    fn property_value(&self, name: &str) -> Result<Option<String>, Error>;

    /// Retrieves a RocksDB property and casts it to an integer.
    ///
    /// For a full list of properties that return int values, see
    /// https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L654-L689
    fn property_int_value(&self, name: &str) -> Result<Option<u64>, Error>;
}

pub trait GetPropertyCF {
    /// Retrieves a RocksDB property by name, for a specific column family.
    ///
    /// For a full list of properties, see
    /// https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L428-L634
    fn property_value_cf(&self, cf: &ColumnFamily, name: &str) -> Result<Option<String>, Error>;

    /// Retrieves a RocksDB property for a specific column family and casts it to an integer.
    ///
    /// For a full list of properties that return int values, see
    /// https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L654-L689
    fn property_int_value_cf(&self, cf: &ColumnFamily, name: &str) -> Result<Option<u64>, Error>;
}

impl<T> GetProperty for T
where
    T: Handle<ffi::rocksdb_t>,
{
    fn property_value(&self, name: &str) -> Result<Option<String>, Error> {
        property_value_impl(
            name,
            |prop_name| unsafe { ffi::rocksdb_property_value(self.handle(), prop_name) },
            |str_value| Ok(str_value.to_owned()),
        )
    }

    fn property_int_value(&self, name: &str) -> Result<Option<u64>, Error> {
        property_value_impl(
            name,
            |prop_name| unsafe { ffi::rocksdb_property_value(self.handle(), prop_name) },
            parse_property_int_value,
        )
    }
}

impl<T> GetPropertyCF for T
where
    T: Handle<ffi::rocksdb_t>,
{
    fn property_value_cf(&self, cf: &ColumnFamily, name: &str) -> Result<Option<String>, Error> {
        property_value_impl(
            name,
            |prop_name| unsafe {
                ffi::rocksdb_property_value_cf(self.handle(), cf.inner, prop_name)
            },
            |str_value| Ok(str_value.to_owned()),
        )
    }

    fn property_int_value_cf(&self, cf: &ColumnFamily, name: &str) -> Result<Option<u64>, Error> {
        property_value_impl(
            name,
            |prop_name| unsafe {
                ffi::rocksdb_property_value_cf(self.handle(), cf.inner, prop_name)
            },
            parse_property_int_value,
        )
    }
}

fn parse_property_int_value(value: &str) -> Result<u64, Error> {
    value.parse::<u64>().map_err(|err| {
        Error::new(format!(
            "Failed to convert property value {} to int: {}",
            value, err
        ))
    })
}

/// Implementation for property_value et al methods.
///
/// `name` is the name of the property.  It will be converted into a CString
/// and passed to `get_property` as argument.  `get_property` reads the
/// specified property and either returns NULL or a pointer to a C allocated
/// string; this method takes ownership of that string and will free it at
/// the end.  That string is parsed using `parse` callback which produces
/// the returned result.
fn property_value_impl<R>(
    name: &str,
    get_property: impl FnOnce(*const c_char) -> *mut c_char,
    parse: impl FnOnce(&str) -> Result<R, Error>,
) -> Result<Option<R>, Error> {
    let value = match CString::new(name) {
        Ok(prop_name) => get_property(prop_name.as_ptr()),
        Err(e) => {
            return Err(Error::new(format!(
                "Failed to convert property name to CString: {}",
                e
            )));
        }
    };
    if value.is_null() {
        return Ok(None);
    }
    let result = match unsafe { CStr::from_ptr(value) }.to_str() {
        Ok(s) => parse(s).map(Some),
        Err(e) => Err(Error::new(format!(
            "Failed to convert property value to string: {}",
            e
        ))),
    };
    unsafe {
        ffi::rocksdb_free(value as *mut c_void);
    }
    result
}