1use std::ffi::CStr;
2use std::os::raw::c_void;
3
4use crate::error::{check_error, to_cstring, Error, ErrorCode, Result};
5use crate::types::DataType;
6
7pub struct Doc {
12 pub(crate) handle: *mut zvec_rust_sys::zvec_doc_t,
13 owned: bool,
14}
15
16impl Doc {
17 pub unsafe fn as_raw(&self) -> *mut zvec_rust_sys::zvec_doc_t {
22 self.handle
23 }
24
25 pub unsafe fn from_raw(handle: *mut zvec_rust_sys::zvec_doc_t) -> Self {
31 Doc {
32 handle,
33 owned: true,
34 }
35 }
36
37 pub fn new() -> Result<Self> {
39 let handle = unsafe { zvec_rust_sys::zvec_doc_create() };
40 if handle.is_null() {
41 return Err(Error {
42 code: ErrorCode::InternalError,
43 message: "failed to create document".into(),
44 });
45 }
46 Ok(Doc {
47 handle,
48 owned: true,
49 })
50 }
51
52 #[allow(dead_code)]
54 pub(crate) fn from_borrowed(handle: *mut zvec_rust_sys::zvec_doc_t) -> Self {
55 Doc {
56 handle,
57 owned: false,
58 }
59 }
60
61 pub fn set_pk(&mut self, pk: &str) {
68 self.try_set_pk(pk).expect("pk must not contain null bytes");
69 }
70
71 pub fn try_set_pk(&mut self, pk: &str) -> Result<()> {
76 let c_pk = to_cstring(pk)?;
77 unsafe { zvec_rust_sys::zvec_doc_set_pk(self.handle, c_pk.as_ptr()) };
78 Ok(())
79 }
80
81 pub fn get_pk(&self) -> Option<&str> {
83 unsafe {
84 let ptr = zvec_rust_sys::zvec_doc_get_pk_pointer(self.handle);
85 if ptr.is_null() {
86 None
87 } else {
88 CStr::from_ptr(ptr).to_str().ok()
89 }
90 }
91 }
92
93 pub fn get_score(&self) -> f32 {
95 unsafe { zvec_rust_sys::zvec_doc_get_score(self.handle) }
96 }
97
98 #[allow(dead_code)]
99 pub(crate) fn get_doc_id(&self) -> u64 {
100 unsafe { zvec_rust_sys::zvec_doc_get_doc_id(self.handle) }
101 }
102
103 pub fn field_count(&self) -> usize {
105 unsafe { zvec_rust_sys::zvec_doc_get_field_count(self.handle) }
106 }
107
108 pub fn is_empty(&self) -> bool {
110 unsafe { zvec_rust_sys::zvec_doc_is_empty(self.handle) }
111 }
112
113 pub fn has_field(&self, name: &str) -> bool {
115 let c_name = match to_cstring(name) {
116 Ok(s) => s,
117 Err(_) => return false,
118 };
119 unsafe { zvec_rust_sys::zvec_doc_has_field(self.handle, c_name.as_ptr()) }
120 }
121
122 pub fn is_field_null(&self, name: &str) -> bool {
124 let c_name = match to_cstring(name) {
125 Ok(s) => s,
126 Err(_) => return false,
127 };
128 unsafe { zvec_rust_sys::zvec_doc_is_field_null(self.handle, c_name.as_ptr()) }
129 }
130
131 pub fn add_string(&mut self, name: &str, value: &str) -> Result<()> {
137 let c_name = to_cstring(name)?;
138 let c_value = to_cstring(value)?;
139 let bytes = c_value.as_bytes();
144 check_error(unsafe {
145 zvec_rust_sys::zvec_doc_add_field_by_value(
146 self.handle,
147 c_name.as_ptr(),
148 DataType::String as u32,
149 bytes.as_ptr() as *const c_void,
150 bytes.len(),
151 )
152 })
153 }
154
155 pub fn add_bool(&mut self, name: &str, value: bool) -> Result<()> {
157 let c_name = to_cstring(name)?;
158 check_error(unsafe {
159 zvec_rust_sys::zvec_doc_add_field_by_value(
160 self.handle,
161 c_name.as_ptr(),
162 DataType::Bool as u32,
163 &value as *const bool as *const c_void,
164 std::mem::size_of::<bool>(),
165 )
166 })
167 }
168
169 pub fn add_i32(&mut self, name: &str, value: i32) -> Result<()> {
171 let c_name = to_cstring(name)?;
172 check_error(unsafe {
173 zvec_rust_sys::zvec_doc_add_field_by_value(
174 self.handle,
175 c_name.as_ptr(),
176 DataType::Int32 as u32,
177 &value as *const i32 as *const c_void,
178 std::mem::size_of::<i32>(),
179 )
180 })
181 }
182
183 pub fn add_i64(&mut self, name: &str, value: i64) -> Result<()> {
185 let c_name = to_cstring(name)?;
186 check_error(unsafe {
187 zvec_rust_sys::zvec_doc_add_field_by_value(
188 self.handle,
189 c_name.as_ptr(),
190 DataType::Int64 as u32,
191 &value as *const i64 as *const c_void,
192 std::mem::size_of::<i64>(),
193 )
194 })
195 }
196
197 pub fn add_u32(&mut self, name: &str, value: u32) -> Result<()> {
199 let c_name = to_cstring(name)?;
200 check_error(unsafe {
201 zvec_rust_sys::zvec_doc_add_field_by_value(
202 self.handle,
203 c_name.as_ptr(),
204 DataType::Uint32 as u32,
205 &value as *const u32 as *const c_void,
206 std::mem::size_of::<u32>(),
207 )
208 })
209 }
210
211 pub fn add_u64(&mut self, name: &str, value: u64) -> Result<()> {
213 let c_name = to_cstring(name)?;
214 check_error(unsafe {
215 zvec_rust_sys::zvec_doc_add_field_by_value(
216 self.handle,
217 c_name.as_ptr(),
218 DataType::Uint64 as u32,
219 &value as *const u64 as *const c_void,
220 std::mem::size_of::<u64>(),
221 )
222 })
223 }
224
225 pub fn add_f32(&mut self, name: &str, value: f32) -> Result<()> {
227 let c_name = to_cstring(name)?;
228 check_error(unsafe {
229 zvec_rust_sys::zvec_doc_add_field_by_value(
230 self.handle,
231 c_name.as_ptr(),
232 DataType::Float as u32,
233 &value as *const f32 as *const c_void,
234 std::mem::size_of::<f32>(),
235 )
236 })
237 }
238
239 pub fn add_f64(&mut self, name: &str, value: f64) -> Result<()> {
241 let c_name = to_cstring(name)?;
242 check_error(unsafe {
243 zvec_rust_sys::zvec_doc_add_field_by_value(
244 self.handle,
245 c_name.as_ptr(),
246 DataType::Double as u32,
247 &value as *const f64 as *const c_void,
248 std::mem::size_of::<f64>(),
249 )
250 })
251 }
252
253 pub fn add_vector_f32(&mut self, name: &str, vector: &[f32]) -> Result<()> {
255 let c_name = to_cstring(name)?;
256 check_error(unsafe {
257 zvec_rust_sys::zvec_doc_add_field_by_value(
258 self.handle,
259 c_name.as_ptr(),
260 DataType::VectorFp32 as u32,
261 vector.as_ptr() as *const c_void,
262 std::mem::size_of_val(vector),
263 )
264 })
265 }
266
267 pub fn add_vector_f64(&mut self, name: &str, vector: &[f64]) -> Result<()> {
269 let c_name = to_cstring(name)?;
270 check_error(unsafe {
271 zvec_rust_sys::zvec_doc_add_field_by_value(
272 self.handle,
273 c_name.as_ptr(),
274 DataType::VectorFp64 as u32,
275 vector.as_ptr() as *const c_void,
276 std::mem::size_of_val(vector),
277 )
278 })
279 }
280
281 pub fn add_binary(&mut self, name: &str, value: &[u8]) -> Result<()> {
283 let c_name = to_cstring(name)?;
284 check_error(unsafe {
285 zvec_rust_sys::zvec_doc_add_field_by_value(
286 self.handle,
287 c_name.as_ptr(),
288 DataType::Binary as u32,
289 value.as_ptr() as *const c_void,
290 value.len(),
291 )
292 })
293 }
294
295 pub fn add_vector_i8(&mut self, name: &str, vector: &[i8]) -> Result<()> {
297 let c_name = to_cstring(name)?;
298 check_error(unsafe {
299 zvec_rust_sys::zvec_doc_add_field_by_value(
300 self.handle,
301 c_name.as_ptr(),
302 DataType::VectorInt8 as u32,
303 vector.as_ptr() as *const c_void,
304 std::mem::size_of_val(vector),
305 )
306 })
307 }
308
309 pub fn add_vector_i16(&mut self, name: &str, vector: &[i16]) -> Result<()> {
311 let c_name = to_cstring(name)?;
312 check_error(unsafe {
313 zvec_rust_sys::zvec_doc_add_field_by_value(
314 self.handle,
315 c_name.as_ptr(),
316 DataType::VectorInt16 as u32,
317 vector.as_ptr() as *const c_void,
318 std::mem::size_of_val(vector),
319 )
320 })
321 }
322
323 fn add_typed_array<T>(&mut self, name: &str, data_type: DataType, values: &[T]) -> Result<()> {
328 let c_name = to_cstring(name)?;
329 check_error(unsafe {
330 zvec_rust_sys::zvec_doc_add_field_by_value(
331 self.handle,
332 c_name.as_ptr(),
333 data_type as u32,
334 values.as_ptr() as *const c_void,
335 std::mem::size_of_val(values),
336 )
337 })
338 }
339
340 pub fn add_array_i32(&mut self, name: &str, values: &[i32]) -> Result<()> {
342 self.add_typed_array(name, DataType::ArrayInt32, values)
343 }
344
345 pub fn add_array_i64(&mut self, name: &str, values: &[i64]) -> Result<()> {
347 self.add_typed_array(name, DataType::ArrayInt64, values)
348 }
349
350 pub fn add_array_u32(&mut self, name: &str, values: &[u32]) -> Result<()> {
352 self.add_typed_array(name, DataType::ArrayUint32, values)
353 }
354
355 pub fn add_array_u64(&mut self, name: &str, values: &[u64]) -> Result<()> {
357 self.add_typed_array(name, DataType::ArrayUint64, values)
358 }
359
360 pub fn add_array_f32(&mut self, name: &str, values: &[f32]) -> Result<()> {
362 self.add_typed_array(name, DataType::ArrayFloat, values)
363 }
364
365 pub fn add_array_f64(&mut self, name: &str, values: &[f64]) -> Result<()> {
367 self.add_typed_array(name, DataType::ArrayDouble, values)
368 }
369
370 pub fn add_array_bool(&mut self, name: &str, values: &[bool]) -> Result<()> {
372 self.add_typed_array(name, DataType::ArrayBool, values)
373 }
374
375 pub fn set_field_null(&mut self, name: &str) -> Result<()> {
377 let c_name = to_cstring(name)?;
378 check_error(unsafe { zvec_rust_sys::zvec_doc_set_field_null(self.handle, c_name.as_ptr()) })
379 }
380
381 pub fn remove_field(&mut self, name: &str) -> Result<()> {
383 let c_name = to_cstring(name)?;
384 check_error(unsafe { zvec_rust_sys::zvec_doc_remove_field(self.handle, c_name.as_ptr()) })
385 }
386
387 fn get_basic_field<T: Copy + Default>(
392 &self,
393 name: &str,
394 data_type: DataType,
395 ) -> Result<Option<T>> {
396 if !self.has_field(name) || self.is_field_null(name) {
397 return Ok(None);
398 }
399 let c_name = to_cstring(name)?;
400 let mut value: T = T::default();
401 check_error(unsafe {
402 zvec_rust_sys::zvec_doc_get_field_value_basic(
403 self.handle,
404 c_name.as_ptr(),
405 data_type as u32,
406 &mut value as *mut T as *mut c_void,
407 std::mem::size_of::<T>(),
408 )
409 })?;
410 Ok(Some(value))
411 }
412
413 fn get_pointer_field(
414 &self,
415 name: &str,
416 data_type: DataType,
417 ) -> Result<Option<(*const c_void, usize)>> {
418 let c_name = to_cstring(name)?;
419 let mut value_ptr: *const c_void = std::ptr::null();
420 let mut value_size: usize = 0;
421 check_error(unsafe {
422 zvec_rust_sys::zvec_doc_get_field_value_pointer(
423 self.handle,
424 c_name.as_ptr(),
425 data_type as u32,
426 &mut value_ptr,
427 &mut value_size,
428 )
429 })?;
430 if value_ptr.is_null() || value_size == 0 {
431 return Ok(None);
432 }
433 Ok(Some((value_ptr, value_size)))
434 }
435
436 fn get_typed_vec<T: Copy>(&self, name: &str, data_type: DataType) -> Result<Option<Vec<T>>> {
437 let Some((ptr, size)) = self.get_pointer_field(name, data_type)? else {
438 return Ok(None);
439 };
440 let elem_size = std::mem::size_of::<T>();
441 if elem_size > 1 && size % elem_size != 0 {
442 return Err(Error {
443 code: ErrorCode::InternalError,
444 message: format!(
445 "data size {} is not aligned to element size {}",
446 size, elem_size
447 ),
448 });
449 }
450 let count = size / elem_size;
451 let slice = unsafe { std::slice::from_raw_parts(ptr as *const T, count) };
452 Ok(Some(slice.to_vec()))
453 }
454
455 pub fn get_string(&self, name: &str) -> Result<Option<String>> {
457 let Some((ptr, _size)) = self.get_pointer_field(name, DataType::String)? else {
458 return Ok(None);
459 };
460 unsafe {
461 let cstr = CStr::from_ptr(ptr as *const std::os::raw::c_char);
462 Ok(Some(cstr.to_string_lossy().into_owned()))
463 }
464 }
465
466 pub fn get_bool(&self, name: &str) -> Result<Option<bool>> {
468 self.get_basic_field(name, DataType::Bool)
469 }
470
471 pub fn get_i32(&self, name: &str) -> Result<Option<i32>> {
473 self.get_basic_field(name, DataType::Int32)
474 }
475
476 pub fn get_i64(&self, name: &str) -> Result<Option<i64>> {
478 self.get_basic_field(name, DataType::Int64)
479 }
480
481 pub fn get_u32(&self, name: &str) -> Result<Option<u32>> {
483 self.get_basic_field(name, DataType::Uint32)
484 }
485
486 pub fn get_u64(&self, name: &str) -> Result<Option<u64>> {
488 self.get_basic_field(name, DataType::Uint64)
489 }
490
491 pub fn get_f32(&self, name: &str) -> Result<Option<f32>> {
493 self.get_basic_field(name, DataType::Float)
494 }
495
496 pub fn get_f64(&self, name: &str) -> Result<Option<f64>> {
498 self.get_basic_field(name, DataType::Double)
499 }
500
501 pub fn get_vector_f32(&self, name: &str) -> Result<Option<Vec<f32>>> {
503 self.get_typed_vec(name, DataType::VectorFp32)
504 }
505
506 pub fn get_vector_f64(&self, name: &str) -> Result<Option<Vec<f64>>> {
508 self.get_typed_vec(name, DataType::VectorFp64)
509 }
510
511 pub fn get_binary(&self, name: &str) -> Result<Option<Vec<u8>>> {
513 self.get_typed_vec(name, DataType::Binary)
514 }
515
516 pub fn get_vector_i8(&self, name: &str) -> Result<Option<Vec<i8>>> {
518 self.get_typed_vec(name, DataType::VectorInt8)
519 }
520
521 pub fn get_vector_i16(&self, name: &str) -> Result<Option<Vec<i16>>> {
523 self.get_typed_vec(name, DataType::VectorInt16)
524 }
525
526 pub fn get_array_i32(&self, name: &str) -> Result<Option<Vec<i32>>> {
532 self.get_typed_vec(name, DataType::ArrayInt32)
533 }
534
535 pub fn get_array_i64(&self, name: &str) -> Result<Option<Vec<i64>>> {
537 self.get_typed_vec(name, DataType::ArrayInt64)
538 }
539
540 pub fn get_array_u32(&self, name: &str) -> Result<Option<Vec<u32>>> {
542 self.get_typed_vec(name, DataType::ArrayUint32)
543 }
544
545 pub fn get_array_u64(&self, name: &str) -> Result<Option<Vec<u64>>> {
547 self.get_typed_vec(name, DataType::ArrayUint64)
548 }
549
550 pub fn get_array_f32(&self, name: &str) -> Result<Option<Vec<f32>>> {
552 self.get_typed_vec(name, DataType::ArrayFloat)
553 }
554
555 pub fn get_array_f64(&self, name: &str) -> Result<Option<Vec<f64>>> {
557 self.get_typed_vec(name, DataType::ArrayDouble)
558 }
559
560 pub fn get_array_bool(&self, name: &str) -> Result<Option<Vec<bool>>> {
562 self.get_typed_vec(name, DataType::ArrayBool)
563 }
564
565 pub fn clear(&mut self) {
567 unsafe { zvec_rust_sys::zvec_doc_clear(self.handle) };
568 }
569}
570
571impl Drop for Doc {
572 fn drop(&mut self) {
573 if self.owned && !self.handle.is_null() {
574 unsafe { zvec_rust_sys::zvec_doc_destroy(self.handle) };
575 }
576 }
577}
578
579pub fn free_docs(docs: Vec<Doc>) {
581 drop(docs);
583}
584
585#[cfg(test)]
586mod tests {
587 use super::*;
588
589 #[test]
590 fn from_borrowed_does_not_own() {
591 let doc = Doc::from_borrowed(std::ptr::null_mut());
593 assert!(!doc.owned);
594 assert!(doc.handle.is_null());
595 }
596
597 #[test]
598 fn from_raw_takes_ownership() {
599 let doc = unsafe { Doc::from_raw(std::ptr::null_mut()) };
600 assert!(doc.owned);
601 }
603
604 #[test]
605 fn try_set_pk_accepts_plain_key() {
606 let mut doc = Doc::new().expect("create doc");
607 doc.try_set_pk("pk-1").expect("plain pk is accepted");
608 assert_eq!(doc.get_pk(), Some("pk-1"));
609 }
610
611 #[test]
612 fn try_set_pk_rejects_interior_nul() {
613 let mut doc = Doc::new().expect("create doc");
614 let err = doc
615 .try_set_pk("pk\0-1")
616 .expect_err("an interior nul byte must be rejected");
617 assert_eq!(err.code, ErrorCode::InvalidArgument);
618 }
619
620 #[test]
621 #[should_panic(expected = "pk must not contain null bytes")]
622 fn set_pk_panics_on_interior_nul() {
623 let mut doc = Doc::new().expect("create doc");
624 doc.set_pk("pk\0-1");
625 }
626}