use std::{
cmp::Ordering,
fs::File,
io::{self, BufReader},
ops::{Deref, DerefMut},
path::Path,
};
use serde::{Deserialize, Serialize};
use crate::{path_utils::glob_rotated_logs, Multireader};
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct InodeAwareOffset {
pub inode: u64,
pub offset: u64,
}
pub struct InodeAwareReader {
inner: Multireader<BufReader<File>>,
inodes: Vec<u64>,
}
impl InodeAwareReader {
pub fn from_rotated_logs(path: impl AsRef<Path>) -> io::Result<Self> {
Self::from_rotated_logs_with_depth(path, 2)
}
pub fn from_rotated_logs_with_depth(
path: impl AsRef<Path>,
max_depth: usize,
) -> io::Result<Self> {
let paths_and_inodes = glob_rotated_logs(path, max_depth)?;
let (paths, inodes): (Vec<_>, Vec<_>) = paths_and_inodes.into_iter().unzip();
let files = paths
.into_iter()
.map(|path| -> io::Result<BufReader<File>> { Ok(BufReader::new(File::open(path)?)) })
.collect::<io::Result<Vec<BufReader<File>>>>()?;
let multireader = Multireader::new(files)?;
Ok(Self {
inner: multireader,
inodes,
})
}
pub fn get_persistent_offset(&self) -> InodeAwareOffset {
let inode = self.get_current_inode();
let offset = self.get_local_offset();
InodeAwareOffset { inode, offset }
}
pub fn seek_persistent(&mut self, offset: InodeAwareOffset) -> io::Result<()> {
let Some(inode_index) = self.get_item_index_by_inode(offset.inode) else {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"provided inode does not exist",
));
};
self.seek_by_local_index(inode_index, io::SeekFrom::Start(offset.offset))?;
Ok(())
}
pub fn get_inodes(&self) -> &[u64] {
&self.inodes
}
pub fn into_inner(self) -> (Multireader<BufReader<File>>, Vec<u64>) {
(self.inner, self.inodes)
}
pub fn get_current_inode(&self) -> u64 {
let item_index = self.get_current_item_index();
self.inodes[item_index]
}
pub fn get_item_index_by_inode(&self, inode: u64) -> Option<usize> {
self.get_inodes()
.iter()
.cloned()
.enumerate()
.find(|&(_, i)| i == inode)
.map(|(idx, _)| idx)
}
pub fn compare_offsets(
&self,
first: InodeAwareOffset,
second: InodeAwareOffset,
) -> Option<Ordering> {
let first_index = self.get_item_index_by_inode(first.inode)?;
let second_index = self.get_item_index_by_inode(second.inode)?;
if first_index == second_index {
return Some(first.offset.cmp(&second.offset));
}
Some(first_index.cmp(&second_index))
}
}
impl Deref for InodeAwareReader {
type Target = Multireader<BufReader<File>>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for InodeAwareReader {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}