rust_rocksdb/
prop_name.rs1use crate::ffi_util::CStrLike;
2
3use std::ffi::{CStr, CString};
4
5#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
10#[repr(transparent)]
11pub struct PropName(CStr);
12
13impl PropName {
14 pub(crate) const fn new_unwrap(value: &str) -> &Self {
20 if let Some((&0, bytes)) = value.as_bytes().split_last() {
21 let mut idx = 0;
22 while idx < bytes.len() {
23 assert!(bytes[idx] != 0, "input contained interior nul byte");
24 idx += 1;
25 }
26
27 unsafe {
31 let value = CStr::from_bytes_with_nul_unchecked(value.as_bytes());
32 &*(std::ptr::from_ref::<CStr>(value) as *const Self)
33 }
34 } else {
35 panic!("input was not nul-terminated");
36 }
37 }
38
39 #[inline]
41 pub fn as_c_str(&self) -> &CStr {
42 &self.0
43 }
44
45 #[inline]
50 pub fn as_str(&self) -> &str {
51 unsafe { std::str::from_utf8_unchecked(self.0.to_bytes()) }
53 }
54}
55
56impl core::ops::Deref for PropName {
57 type Target = CStr;
58
59 #[inline]
60 fn deref(&self) -> &Self::Target {
61 self.as_c_str()
62 }
63}
64
65impl core::convert::AsRef<CStr> for PropName {
66 #[inline]
67 fn as_ref(&self) -> &CStr {
68 self.as_c_str()
69 }
70}
71
72impl core::convert::AsRef<str> for PropName {
73 #[inline]
74 fn as_ref(&self) -> &str {
75 self.as_str()
76 }
77}
78
79impl std::borrow::ToOwned for PropName {
80 type Owned = PropertyName;
81
82 #[inline]
83 fn to_owned(&self) -> Self::Owned {
84 PropertyName(self.0.to_owned())
85 }
86
87 #[inline]
88 fn clone_into(&self, target: &mut Self::Owned) {
89 self.0.clone_into(&mut target.0);
90 }
91}
92
93impl core::fmt::Display for PropName {
94 #[inline]
95 fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96 self.as_str().fmt(fmtr)
97 }
98}
99
100impl core::fmt::Debug for PropName {
101 #[inline]
102 fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
103 self.as_str().fmt(fmtr)
104 }
105}
106
107impl core::cmp::PartialEq<CStr> for PropName {
108 #[inline]
109 fn eq(&self, other: &CStr) -> bool {
110 self.as_c_str().eq(other)
111 }
112}
113
114impl core::cmp::PartialEq<str> for PropName {
115 #[inline]
116 fn eq(&self, other: &str) -> bool {
117 self.as_str().eq(other)
118 }
119}
120
121impl core::cmp::PartialEq<PropName> for CStr {
122 #[inline]
123 fn eq(&self, other: &PropName) -> bool {
124 self.eq(other.as_c_str())
125 }
126}
127
128impl core::cmp::PartialEq<PropName> for str {
129 #[inline]
130 fn eq(&self, other: &PropName) -> bool {
131 self.eq(other.as_str())
132 }
133}
134
135impl<'a> CStrLike for &'a PropName {
136 type Baked = &'a CStr;
137 type Error = std::convert::Infallible;
138
139 #[inline]
140 fn bake(self) -> Result<Self::Baked, Self::Error> {
141 Ok(&self.0)
142 }
143
144 #[inline]
145 fn into_c_string(self) -> Result<CString, Self::Error> {
146 Ok(self.0.to_owned())
147 }
148}
149
150#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
155#[repr(transparent)]
156pub struct PropertyName(CString);
157
158impl PropertyName {
159 #[inline]
162 unsafe fn from_vec_with_nul_unchecked(inner: Vec<u8>) -> Self {
163 unsafe {
164 Self(CString::from_vec_with_nul_unchecked(inner))
166 }
167 }
168
169 #[inline]
171 pub fn into_c_string(self) -> CString {
172 self.0
173 }
174
175 #[inline]
180 pub fn into_string(self) -> String {
181 unsafe { String::from_utf8_unchecked(self.0.into_bytes()) }
183 }
184}
185
186impl std::ops::Deref for PropertyName {
187 type Target = PropName;
188
189 #[inline]
190 fn deref(&self) -> &Self::Target {
191 unsafe { &*(std::ptr::from_ref::<CStr>(self.0.as_c_str()) as *const PropName) }
195 }
196}
197
198impl core::convert::AsRef<CStr> for PropertyName {
199 #[inline]
200 fn as_ref(&self) -> &CStr {
201 self.as_c_str()
202 }
203}
204
205impl core::convert::AsRef<str> for PropertyName {
206 #[inline]
207 fn as_ref(&self) -> &str {
208 self.as_str()
209 }
210}
211
212impl std::borrow::Borrow<PropName> for PropertyName {
213 #[inline]
214 fn borrow(&self) -> &PropName {
215 self
216 }
217}
218
219impl core::fmt::Display for PropertyName {
220 #[inline]
221 fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
222 self.as_str().fmt(fmtr)
223 }
224}
225
226impl core::fmt::Debug for PropertyName {
227 #[inline]
228 fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
229 self.as_str().fmt(fmtr)
230 }
231}
232
233impl core::cmp::PartialEq<CString> for PropertyName {
234 #[inline]
235 fn eq(&self, other: &CString) -> bool {
236 self.as_c_str().eq(other.as_c_str())
237 }
238}
239
240impl core::cmp::PartialEq<String> for PropertyName {
241 #[inline]
242 fn eq(&self, other: &String) -> bool {
243 self.as_str().eq(other.as_str())
244 }
245}
246
247impl core::cmp::PartialEq<PropertyName> for CString {
248 #[inline]
249 fn eq(&self, other: &PropertyName) -> bool {
250 self.as_c_str().eq(other.as_c_str())
251 }
252}
253
254impl core::cmp::PartialEq<PropertyName> for String {
255 #[inline]
256 fn eq(&self, other: &PropertyName) -> bool {
257 self.as_str().eq(other.as_str())
258 }
259}
260
261impl CStrLike for PropertyName {
262 type Baked = CString;
263 type Error = std::convert::Infallible;
264
265 #[inline]
266 fn bake(self) -> Result<Self::Baked, Self::Error> {
267 Ok(self.0)
268 }
269
270 #[inline]
271 fn into_c_string(self) -> Result<CString, Self::Error> {
272 Ok(self.0)
273 }
274}
275
276impl<'a> CStrLike for &'a PropertyName {
277 type Baked = &'a CStr;
278 type Error = std::convert::Infallible;
279
280 #[inline]
281 fn bake(self) -> Result<Self::Baked, Self::Error> {
282 Ok(self.as_c_str())
283 }
284
285 #[inline]
286 fn into_c_string(self) -> Result<CString, Self::Error> {
287 Ok(self.0.clone())
288 }
289}
290
291pub(crate) unsafe fn level_property(name: &str, level: usize) -> PropertyName {
299 unsafe {
300 let bytes = format!("rocksdb.{name}{level}\0").into_bytes();
301 PropertyName::from_vec_with_nul_unchecked(bytes)
304 }
305}
306
307#[test]
308fn sanity_checks() {
309 let want = "rocksdb.cfstats-no-file-histogram";
310 assert_eq!(want, crate::properties::CFSTATS_NO_FILE_HISTOGRAM);
311
312 let want = "rocksdb.num-files-at-level5";
313 assert_eq!(want, &*crate::properties::num_files_at_level(5));
314}
315
316#[test]
317#[should_panic(expected = "input contained interior nul byte")]
318fn test_interior_nul() {
319 PropName::new_unwrap("interior nul\0\0");
320}
321
322#[test]
323#[should_panic(expected = "input was not nul-terminated")]
324fn test_non_nul_terminated() {
325 PropName::new_unwrap("no nul terminator");
326}