use std::{fs, io::Read, path::Path};
pub mod binary;
pub mod json;
pub mod load;
mod save;
#[cfg(test)]
mod tests;
impl crate::Database {
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, crate::StorageError> {
let path_ref = path.as_ref();
let format = detect_format(path_ref)?;
match format {
PersistenceFormat::Binary => Self::load_binary(path),
PersistenceFormat::BinaryCompressed => Self::load_compressed(path),
PersistenceFormat::Json => Self::load_json(path),
PersistenceFormat::Sql => {
Err(crate::StorageError::NotImplemented(
"SQL dump loading requires the executor layer. \
Use vibesql_executor::load_sql_dump() instead, or use binary format (.vbsql/.vbsqlz) or JSON format (.json)"
.to_string(),
))
}
PersistenceFormat::Sqlite => {
Err(crate::StorageError::NotImplemented(
"SQLite database detected. Use `vibesql import <file.db>` to convert, \
or open directly with `vibesql <file.db>` in the CLI."
.to_string(),
))
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PersistenceFormat {
Sql,
Binary,
BinaryCompressed,
Json,
Sqlite,
}
fn detect_format<P: AsRef<Path>>(path: P) -> Result<PersistenceFormat, crate::StorageError> {
let path_ref = path.as_ref();
if let Some(ext) = path_ref.extension().and_then(|e| e.to_str()) {
match ext {
"vbsqlz" => return Ok(PersistenceFormat::BinaryCompressed),
"json" => return Ok(PersistenceFormat::Json),
"sql" => return Ok(PersistenceFormat::Sql),
_ => {}
}
}
let mut file = fs::File::open(path_ref).map_err(|e| {
crate::StorageError::NotImplemented(format!("Failed to open file {:?}: {}", path_ref, e))
})?;
let mut sqlite_magic = [0u8; 16];
if file.read_exact(&mut sqlite_magic).is_ok() && &sqlite_magic == b"SQLite format 3\0" {
return Ok(PersistenceFormat::Sqlite);
}
file = fs::File::open(path_ref).map_err(|e| {
crate::StorageError::NotImplemented(format!("Failed to open file {:?}: {}", path_ref, e))
})?;
let mut magic = [0u8; 5];
if file.read_exact(&mut magic).is_ok() && &magic == b"VBSQL" {
return Ok(PersistenceFormat::Binary);
}
file = fs::File::open(path_ref).map_err(|e| {
crate::StorageError::NotImplemented(format!("Failed to open file {:?}: {}", path_ref, e))
})?;
let mut zstd_magic = [0u8; 4];
if file.read_exact(&mut zstd_magic).is_ok() && &zstd_magic == b"\x28\xB5\x2F\xFD" {
return Ok(PersistenceFormat::BinaryCompressed);
}
file = fs::File::open(path_ref).map_err(|e| {
crate::StorageError::NotImplemented(format!("Failed to open file {:?}: {}", path_ref, e))
})?;
let mut first_byte = [0u8; 1];
if file.read_exact(&mut first_byte).is_ok() && first_byte[0] == b'{' {
return Ok(PersistenceFormat::Json);
}
Ok(PersistenceFormat::Sql)
}