use std::fs::File;
use std::path::Path;
use crate::error::{Result, SqliteError};
pub trait Source {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()>;
fn len(&self) -> Result<u64>;
fn is_empty(&self) -> Result<bool> {
Ok(self.len()? == 0)
}
fn accounts_for_wal(&self) -> bool {
false
}
}
pub struct FileSource {
file: File,
no_log_beside_it: bool,
}
impl FileSource {
pub fn open(path: impl AsRef<Path>) -> Result<Self> {
let path = path.as_ref();
let file = File::open(path)?;
let mut log = path.as_os_str().to_os_string();
log.push("-wal");
let no_log_beside_it = match std::fs::metadata(Path::new(&log)) {
Ok(meta) => meta.len() == 0,
Err(_) => true,
};
Ok(FileSource {
file,
no_log_beside_it,
})
}
}
#[cfg(unix)]
impl Source for FileSource {
fn accounts_for_wal(&self) -> bool {
self.no_log_beside_it
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
use std::os::unix::fs::FileExt;
self.file.read_exact_at(buf, offset)?;
Ok(())
}
fn len(&self) -> Result<u64> {
Ok(self.file.metadata()?.len())
}
}
#[cfg(windows)]
impl Source for FileSource {
fn accounts_for_wal(&self) -> bool {
self.no_log_beside_it
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
use std::os::windows::fs::FileExt;
let mut done = 0;
while done < buf.len() {
let n = self
.file
.seek_read(&mut buf[done..], offset + done as u64)?;
if n == 0 {
return Err(SqliteError::Io(std::io::Error::from(
std::io::ErrorKind::UnexpectedEof,
)));
}
done += n;
}
Ok(())
}
fn len(&self) -> Result<u64> {
Ok(self.file.metadata()?.len())
}
}
pub struct SliceSource<'a> {
bytes: &'a [u8],
}
impl<'a> SliceSource<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
SliceSource { bytes }
}
}
impl Source for SliceSource<'_> {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
let start = usize::try_from(offset).map_err(|_| {
SqliteError::Io(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))
})?;
let end = start.checked_add(buf.len()).ok_or_else(|| {
SqliteError::Io(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))
})?;
if end > self.bytes.len() {
return Err(SqliteError::Io(std::io::Error::from(
std::io::ErrorKind::UnexpectedEof,
)));
}
buf.copy_from_slice(&self.bytes[start..end]);
Ok(())
}
fn len(&self) -> Result<u64> {
Ok(self.bytes.len() as u64)
}
}