pub mod attest;
pub mod render;
pub mod schema;
pub mod text_columns;
#[cfg(test)]
mod tests;
use std::path::{Path, PathBuf};
use rusqlite::{Connection, OpenFlags};
use crate::core::config::expand_path;
use crate::core::errors::{Result, TgaError};
pub fn open_read_only(path: &Path) -> Result<Connection> {
let resolved: PathBuf = expand_path(path);
if !resolved.exists() {
return Err(TgaError::NotFound(format!(
"no database at {} — run `tga collect` first, or point --database at an existing tga.db",
resolved.display()
)));
}
if resolved.is_dir() {
return Err(TgaError::ValidationError(format!(
"{} is a directory, not a tga database file",
resolved.display()
)));
}
let header = read_header(&resolved)?;
if !header.starts_with(SQLITE_MAGIC) {
return Err(TgaError::ValidationError(format!(
"{} is not a SQLite database (expected the \"SQLite format 3\" header, \
found {} byte(s) of other content)",
resolved.display(),
header.len()
)));
}
Connection::open_with_flags(
&resolved,
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_URI,
)
.map_err(TgaError::from)
}
const SQLITE_MAGIC: &[u8] = b"SQLite format 3\0";
fn read_header(path: &Path) -> Result<Vec<u8>> {
use std::io::Read;
let mut file = std::fs::File::open(path).map_err(|e| {
TgaError::IoError(std::io::Error::new(
e.kind(),
format!("cannot read {}: {e}", path.display()),
))
})?;
let mut buf = vec![0_u8; SQLITE_MAGIC.len()];
let read = file.read(&mut buf).map_err(|e| {
TgaError::IoError(std::io::Error::new(
e.kind(),
format!("cannot read {}: {e}", path.display()),
))
})?;
buf.truncate(read);
Ok(buf)
}