1use std::{
2 cmp::Ordering,
3 fs::File,
4 io::{self, BufReader},
5 ops::{Deref, DerefMut},
6 path::Path,
7};
8
9use serde::{Deserialize, Serialize};
10
11use crate::{path_utils::glob_rotated_logs, Multireader};
12
13#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
15pub struct InodeAwareOffset {
16 pub inode: u64,
17 pub offset: u64,
18}
19
20pub struct InodeAwareReader {
41 inner: Multireader<BufReader<File>>,
42 inodes: Vec<u64>,
43}
44
45impl InodeAwareReader {
46 pub fn from_rotated_logs(path: impl AsRef<Path>) -> io::Result<Self> {
48 Self::from_rotated_logs_with_depth(path, 2)
49 }
50
51 pub fn from_rotated_logs_with_depth(
53 path: impl AsRef<Path>,
54 max_depth: usize,
55 ) -> io::Result<Self> {
56 let paths_and_inodes = glob_rotated_logs(path, max_depth)?;
57 let (paths, inodes): (Vec<_>, Vec<_>) = paths_and_inodes.into_iter().unzip();
58 let files = paths
59 .into_iter()
60 .map(|path| -> io::Result<BufReader<File>> { Ok(BufReader::new(File::open(path)?)) })
61 .collect::<io::Result<Vec<BufReader<File>>>>()?;
62 let multireader = Multireader::new(files)?;
63
64 Ok(Self {
65 inner: multireader,
66 inodes,
67 })
68 }
69
70 pub fn get_persistent_offset(&self) -> InodeAwareOffset {
72 let inode = self.get_current_inode();
73 let offset = self.get_local_offset();
74 InodeAwareOffset { inode, offset }
75 }
76
77 pub fn seek_persistent(&mut self, offset: InodeAwareOffset) -> io::Result<()> {
81 let Some(inode_index) = self.get_item_index_by_inode(offset.inode) else {
82 return Err(io::Error::new(
83 io::ErrorKind::NotFound,
84 "provided inode does not exist",
85 ));
86 };
87 self.seek_by_local_index(inode_index, io::SeekFrom::Start(offset.offset))?;
88 Ok(())
89 }
90
91 pub fn get_inodes(&self) -> &[u64] {
93 &self.inodes
94 }
95
96 pub fn into_inner(self) -> (Multireader<BufReader<File>>, Vec<u64>) {
98 (self.inner, self.inodes)
99 }
100
101 pub fn get_current_inode(&self) -> u64 {
103 let item_index = self.get_current_item_index();
104 self.inodes[item_index]
105 }
106
107 pub fn get_item_index_by_inode(&self, inode: u64) -> Option<usize> {
109 self.get_inodes()
110 .iter()
111 .cloned()
112 .enumerate()
113 .find(|&(_, i)| i == inode)
114 .map(|(idx, _)| idx)
115 }
116
117 pub fn compare_offsets(
120 &self,
121 first: InodeAwareOffset,
122 second: InodeAwareOffset,
123 ) -> Option<Ordering> {
124 let first_index = self.get_item_index_by_inode(first.inode)?;
125 let second_index = self.get_item_index_by_inode(second.inode)?;
126 if first_index == second_index {
127 return Some(first.offset.cmp(&second.offset));
128 }
129 Some(first_index.cmp(&second_index))
130 }
131}
132
133impl Deref for InodeAwareReader {
134 type Target = Multireader<BufReader<File>>;
135
136 fn deref(&self) -> &Self::Target {
137 &self.inner
138 }
139}
140
141impl DerefMut for InodeAwareReader {
142 fn deref_mut(&mut self) -> &mut Self::Target {
143 &mut self.inner
144 }
145}