Skip to main content

feather_db_cli/
lib.rs

1use std::ffi::{c_void, c_char};
2use std::path::Path;
3
4#[repr(C)]
5pub struct DB(*mut c_void);  // ← `pub`
6
7extern "C" {
8    fn feather_open(path: *const c_char, dim: usize) -> *mut c_void;
9    fn feather_add(db: *mut c_void, id: u64, vec: *const f32, len: usize);
10    fn feather_add_with_meta(db: *mut c_void, id: u64, vec: *const f32, len: usize,
11                              timestamp: i64, importance: f32, context_type: u8,
12                              source: *const c_char, content: *const c_char, modality: *const c_char);
13    fn feather_link(db: *mut c_void, from_id: u64, to_id: u64);
14    fn feather_touch(db: *mut c_void, id: u64);
15    fn feather_search(db: *mut c_void, query: *const f32, len: usize, k: usize,
16                      out_ids: *mut u64, out_dists: *mut f32, modality: *const c_char);
17    fn feather_search_with_filter(db: *mut c_void, query: *const f32, len: usize, k: usize,
18                                   type_filter: u8, source_filter: *const c_char,
19                                   out_ids: *mut u64, out_dists: *mut f32, modality: *const c_char);
20    fn feather_save(db: *mut c_void);
21    fn feather_close(db: *mut c_void);
22}
23
24impl DB {
25    pub fn open(path: &Path, dim: usize) -> Option<Self> {
26        let c_path = std::ffi::CString::new(path.to_str()?).ok()?;
27        let ptr = unsafe { feather_open(c_path.as_ptr(), dim) };
28        if ptr.is_null() { None } else { Some(DB(ptr)) }
29    }
30
31    pub fn add(&self, id: u64, vec: &[f32]) {
32        unsafe { feather_add(self.0, id, vec.as_ptr(), vec.len()) }
33    }
34
35    pub fn add_with_meta(&self, id: u64, vec: &[f32], timestamp: i64, importance: f32, context_type: u8, 
36                         source: Option<&str>, content: Option<&str>, modality: Option<&str>) {
37        let c_source = source.and_then(|s| std::ffi::CString::new(s).ok());
38        let c_content = content.and_then(|s| std::ffi::CString::new(s).ok());
39        let c_modality = modality.and_then(|s| std::ffi::CString::new(s).ok());
40        
41        unsafe {
42            feather_add_with_meta(
43                self.0, id, vec.as_ptr(), vec.len(),
44                timestamp, importance, context_type,
45                c_source.map_or(std::ptr::null(), |s| s.as_ptr()),
46                c_content.map_or(std::ptr::null(), |s| s.as_ptr()),
47                c_modality.map_or(std::ptr::null(), |s| s.as_ptr())
48            )
49        }
50    }
51
52    pub fn link(&self, from_id: u64, to_id: u64) {
53        unsafe { feather_link(self.0, from_id, to_id) }
54    }
55
56    pub fn touch(&self, id: u64) {
57        unsafe { feather_touch(self.0, id) }
58    }
59
60    pub fn search(&self, query: &[f32], k: usize, modality: Option<&str>) -> (Vec<u64>, Vec<f32>) {
61        let mut ids = vec![0u64; k];
62        let mut dists = vec![0f32; k];
63        let c_modality = modality.and_then(|s| std::ffi::CString::new(s).ok());
64        unsafe {
65            feather_search(
66                self.0, query.as_ptr(), query.len(), k, 
67                ids.as_mut_ptr(), dists.as_mut_ptr(),
68                c_modality.map_or(std::ptr::null(), |s| s.as_ptr())
69            )
70        };
71        (ids, dists)
72    }
73
74    pub fn search_with_filter(&self, query: &[f32], k: usize, type_filter: Option<u8>, 
75                               source_filter: Option<&str>, modality: Option<&str>) -> (Vec<u64>, Vec<f32>) {
76        let mut ids = vec![0u64; k];
77        let mut dists = vec![0f32; k];
78        let c_source = source_filter.and_then(|s| std::ffi::CString::new(s).ok());
79        let c_modality = modality.and_then(|s| std::ffi::CString::new(s).ok());
80        
81        unsafe {
82            feather_search_with_filter(
83                self.0, query.as_ptr(), query.len(), k,
84                type_filter.unwrap_or(255),
85                c_source.map_or(std::ptr::null(), |s| s.as_ptr()),
86                ids.as_mut_ptr(), dists.as_mut_ptr(),
87                c_modality.map_or(std::ptr::null(), |s| s.as_ptr())
88            )
89        };
90        (ids, dists)
91    }
92
93    pub fn save(&self) { unsafe { feather_save(self.0) } }
94}
95
96impl Drop for DB {
97    fn drop(&mut self) { unsafe { feather_close(self.0) } }
98}