couchbase_lite_core_sys/
helpers.rs

1//! Code to help deal with C API
2
3use crate::{
4    C4CollectionSpec, C4String, FLHeapSlice, FLSlice, FLSliceResult, FLSliceResult_Release,
5    FLString,
6};
7use std::{borrow::Cow, os::raw::c_void, ptr, slice, str};
8
9impl Default for FLSlice {
10    #[inline]
11    fn default() -> Self {
12        Self {
13            buf: ptr::null(),
14            size: 0,
15        }
16    }
17}
18
19impl<'a> From<&'a str> for FLSlice {
20    #[inline]
21    fn from(s: &'a str) -> Self {
22        Self {
23            buf: if !s.is_empty() {
24                s.as_ptr() as *const c_void
25            } else {
26                ptr::null()
27            },
28            size: s.len(),
29        }
30    }
31}
32
33impl<'a> From<&'a [u8]> for FLSlice {
34    #[inline]
35    fn from(ba: &'a [u8]) -> Self {
36        Self {
37            buf: if !ba.is_empty() {
38                ba.as_ptr() as *const c_void
39            } else {
40                ptr::null()
41            },
42            size: ba.len(),
43        }
44    }
45}
46
47impl From<FLSlice> for &[u8] {
48    #[inline]
49    fn from(s: FLSlice) -> Self {
50        if s.size != 0 {
51            unsafe { slice::from_raw_parts(s.buf as *const u8, s.size) }
52        } else {
53            // pointer should not be null, even in zero case
54            // but pointer from FLSlice can be null in zero case, so:
55            &[]
56        }
57    }
58}
59
60impl Drop for FLSliceResult {
61    #[inline]
62    fn drop(&mut self) {
63        unsafe {
64            FLSliceResult_Release(FLSliceResult {
65                buf: self.buf,
66                size: self.size,
67            })
68        };
69    }
70}
71
72impl Default for FLSliceResult {
73    #[inline]
74    fn default() -> Self {
75        Self {
76            buf: ptr::null(),
77            size: 0,
78        }
79    }
80}
81
82impl FLSliceResult {
83    #[inline]
84    pub fn as_bytes(&self) -> &[u8] {
85        if self.size != 0 {
86            unsafe { slice::from_raw_parts(self.buf as *const u8, self.size) }
87        } else {
88            // pointer should not be null, even in zero case
89            // but pointer from FLSlice can be null in zero case, so:
90            &[]
91        }
92    }
93    #[inline]
94    pub fn as_utf8_lossy(&self) -> Cow<'_, str> {
95        String::from_utf8_lossy(self.as_bytes())
96    }
97    #[inline]
98    pub fn is_empty(&self) -> bool {
99        self.size == 0
100    }
101    #[inline]
102    pub fn as_fl_slice(&self) -> FLSlice {
103        FLSlice {
104            buf: self.buf,
105            size: self.size,
106        }
107    }
108}
109
110impl<'a> TryFrom<FLString> for &'a str {
111    type Error = str::Utf8Error;
112    #[inline]
113    fn try_from(value: FLString) -> Result<Self, Self::Error> {
114        let bytes: &'a [u8] = value.into();
115        str::from_utf8(bytes)
116    }
117}
118
119impl FLHeapSlice {
120    #[inline]
121    pub fn as_fl_slice(&self) -> FLSlice {
122        *self
123    }
124}
125
126// bindgen can not handle these constants properly, so redefine them
127// see https://github.com/rust-lang/rust-bindgen/issues/316
128macro_rules! flstr {
129    ($str:expr) => {
130        C4String {
131            buf: $str.as_bytes().as_ptr() as *const c_void,
132            size: $str.as_bytes().len() - 1,
133        }
134    };
135}
136
137/// #define kC4DefaultScopeID FLSTR("_default")
138#[allow(non_upper_case_globals)]
139pub const kC4DefaultScopeID: C4String = flstr!("_default\0");
140
141/// #define kC4DefaultCollectionName FLSTR("_default")
142#[allow(non_upper_case_globals)]
143pub const kC4DefaultCollectionName: C4String = flstr!("_default\0");
144
145#[allow(non_upper_case_globals)]
146pub const kC4DefaultCollectionSpec: C4CollectionSpec = C4CollectionSpec {
147    name: kC4DefaultCollectionName,
148    scope: kC4DefaultScopeID,
149};
150
151#[test]
152fn test_null_slice_handling() {
153    let ffi_null_slice = FLSlice {
154        buf: ptr::null(),
155        size: 0,
156    };
157    let slice: &[u8] = ffi_null_slice.into();
158    assert!(slice.is_empty());
159
160    let ffi_null_slice: FLSliceResult = unsafe { crate::FLSliceResult_New(0) };
161    let slice: &[u8] = ffi_null_slice.as_bytes();
162    assert!(slice.is_empty());
163
164    let ffi_null_slice = FLSliceResult {
165        buf: ptr::null(),
166        size: 0,
167    };
168    let slice: &[u8] = ffi_null_slice.as_bytes();
169    assert!(slice.is_empty());
170}