pub mod keys;
pub mod writer;
use std::path::{Path, PathBuf};
use fjall::{Database, Keyspace, KeyspaceCreateOptions};
use thiserror::Error;
pub const INDEX_SCHEMA_VER: u32 = crate::version::RELEASE_MINOR as u32;
const META_SCHEMA_VER: &[u8] = b"schema_ver";
const INDEX_DIR: &str = "index.fjall";
#[derive(Debug, Error)]
pub enum IndexError {
#[error("fjall error: {0}")]
Fjall(#[from] fjall::Error),
#[error("io error on {path}: {source}")]
Io {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("msgpack encode error: {0}")]
Encode(#[from] rmp_serde::encode::Error),
#[error("msgpack decode error: {0}")]
Decode(#[from] rmp_serde::decode::Error),
}
#[derive(Clone)]
pub struct IndexDb {
pub(crate) db: Database,
#[allow(dead_code)] pub(crate) meta: Keyspace,
pub(crate) symbols_by_path: Keyspace,
pub(crate) symbols_by_name: Keyspace,
pub(crate) calls_by_path: Keyspace,
pub(crate) calls_by_callee: Keyspace,
pub(crate) imports_by_module: Keyspace,
#[allow(dead_code)] pub(crate) embeddings: Keyspace,
}
impl IndexDb {
pub fn open(view_dir: &Path) -> Result<Self, IndexError> {
let dir = view_dir.join(INDEX_DIR);
let needs_wipe = match peek_schema_version(&dir) {
Some(ver) if ver == INDEX_SCHEMA_VER => false,
None => false, Some(_) => true,
};
if needs_wipe && dir.exists() {
std::fs::remove_dir_all(&dir).map_err(|source| IndexError::Io {
path: dir.clone(),
source,
})?;
}
std::fs::create_dir_all(&dir).map_err(|source| IndexError::Io {
path: dir.clone(),
source,
})?;
let db = Database::builder(&dir).open()?;
let meta = db.keyspace("meta", KeyspaceCreateOptions::default)?;
let symbols_by_path = db.keyspace("symbols_by_path", KeyspaceCreateOptions::default)?;
let symbols_by_name = db.keyspace("symbols_by_name", KeyspaceCreateOptions::default)?;
let calls_by_path = db.keyspace("calls_by_path", KeyspaceCreateOptions::default)?;
let calls_by_callee = db.keyspace("calls_by_callee", KeyspaceCreateOptions::default)?;
let imports_by_module = db.keyspace("imports_by_module", KeyspaceCreateOptions::default)?;
let embeddings = db.keyspace("embeddings", KeyspaceCreateOptions::default)?;
meta.insert(META_SCHEMA_VER, INDEX_SCHEMA_VER.to_be_bytes())?;
Ok(Self {
db,
meta,
symbols_by_path,
symbols_by_name,
calls_by_path,
calls_by_callee,
imports_by_module,
embeddings,
})
}
pub fn writer(&self) -> writer::IndexWriter {
writer::IndexWriter::new(self.clone())
}
}
fn peek_schema_version(dir: &Path) -> Option<u32> {
if !dir.exists() {
return None;
}
let db = Database::builder(dir).open().ok()?;
let meta = db.keyspace("meta", KeyspaceCreateOptions::default).ok()?;
let bytes = meta.get(META_SCHEMA_VER).ok().flatten()?;
if bytes.len() != 4 {
return None;
}
Some(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}