use std::{
fmt::{self, Debug, Formatter},
io,
ops::Range,
sync::Arc,
};
use async_trait::async_trait;
use block_on_place::HandleExt;
use tantivy::{
HasLen,
directory::{FileHandle, OwnedBytes},
};
use tokio::runtime::Handle;
use crate::{context::Context, operator::Operator};
#[derive(Clone)]
pub struct File {
path: String,
backend: Backend,
}
#[derive(Clone)]
enum Backend {
Remote {
rt: Handle,
operator: Operator,
offset: u64,
len: u64,
chunks: Option<usize>,
concurrency: Option<usize>,
},
Memory { bytes: OwnedBytes },
}
impl File {
pub(crate) fn open(
path: impl Into<String>,
len: u64,
rt: Handle,
operator: Operator,
context: &Context,
) -> Arc<Self> {
Arc::new(Self {
path: path.into(),
backend: Backend::Remote {
rt,
operator,
offset: 0,
len,
chunks: context.read_chunks,
concurrency: context.read_concurrency,
},
})
}
pub(crate) fn bundled(
path: impl Into<String>,
offset: u64,
len: u64,
rt: Handle,
operator: Operator,
context: &Context,
) -> Arc<Self> {
Arc::new(Self {
path: path.into(),
backend: Backend::Remote {
rt,
operator,
offset,
len,
chunks: context.read_chunks,
concurrency: context.read_concurrency,
},
})
}
pub(crate) fn memory(path: impl Into<String>, bytes: &'static [u8]) -> Arc<Self> {
Arc::new(Self {
path: path.into(),
backend: Backend::Memory {
bytes: OwnedBytes::new(bytes),
},
})
}
pub(crate) fn memory_owned(path: impl Into<String>, bytes: Vec<u8>) -> Arc<Self> {
Arc::new(Self {
path: path.into(),
backend: Backend::Memory {
bytes: OwnedBytes::new(bytes),
},
})
}
#[inline]
pub fn path(&self) -> &str {
&self.path
}
}
#[async_trait]
impl FileHandle for File {
fn read_bytes(&self, range: Range<usize>) -> io::Result<OwnedBytes> {
match &self.backend {
Backend::Remote { rt, .. } => rt.block_on_place(self.read_bytes_async(range)),
Backend::Memory { bytes } => Ok(bytes.slice(range)),
}
}
async fn read_bytes_async(&self, range: Range<usize>) -> io::Result<OwnedBytes> {
let (operator, offset, chunks, concurrency) = match &self.backend {
Backend::Remote {
operator,
offset,
chunks,
concurrency,
..
} => (operator, *offset, chunks, concurrency),
Backend::Memory { bytes } => return Ok(bytes.slice(range)),
};
let mut reader = operator.reader_with(&self.path);
if let Some(chunks) = chunks {
reader = reader.chunk(*chunks);
}
if let Some(concurrency) = concurrency {
reader = reader.concurrent(*concurrency);
}
let reader = reader.await.map_err(io::Error::other)?;
let range = Range {
start: offset + range.start as u64,
end: offset + range.end as u64,
};
let buffer = reader.read(range).await.map_err(io::Error::other)?;
let bytes = buffer.to_vec();
let bytes = OwnedBytes::new(bytes);
Ok(bytes)
}
}
impl HasLen for File {
fn len(&self) -> usize {
match &self.backend {
Backend::Remote { len, .. } => *len as usize,
Backend::Memory { bytes } => bytes.len(),
}
}
}
impl Debug for File {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut f = f.debug_struct("File");
f.field("path", &self.path);
match &self.backend {
Backend::Remote { offset, len, .. } => f.field("offset", offset).field("len", len),
Backend::Memory { bytes } => f.field("memory_len", &bytes.len()),
};
f.finish()
}
}
pub(crate) struct Slice {
inner: Arc<dyn FileHandle>,
offset: usize,
len: usize,
}
impl Slice {
pub fn new(inner: Arc<dyn FileHandle>, offset: usize, len: usize) -> Arc<Self> {
Arc::new(Self { inner, offset, len })
}
}
#[async_trait]
impl FileHandle for Slice {
fn read_bytes(&self, range: Range<usize>) -> io::Result<OwnedBytes> {
self.inner
.read_bytes(self.offset + range.start..self.offset + range.end)
}
async fn read_bytes_async(&self, range: Range<usize>) -> io::Result<OwnedBytes> {
self.inner
.read_bytes_async(self.offset + range.start..self.offset + range.end)
.await
}
}
impl HasLen for Slice {
fn len(&self) -> usize {
self.len
}
}
impl Debug for Slice {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("Slice")
.field("offset", &self.offset)
.field("len", &self.len)
.finish()
}
}