tail/
lib.rs

1use std::fs::{File, Metadata};
2use std::io::{Seek, BufReader, SeekFrom, Read, BufWriter, Write};
3use std::collections::{VecDeque};
4
5const BUFFER_SIZE: u64 = 4096;
6
7pub enum ModificationType {
8    Added,
9    Removed,
10    NoChange,
11}
12
13#[allow(dead_code)]
14pub enum Input {
15    File(File),
16    Stdin(std::io::Stdin),
17}
18
19/// Reads file backwards to efficiently retrieve the last N lines
20///
21/// # Examples
22///
23/// ```
24/// let mut writer = BufWriter::new(std::io::stdout());
25/// let mut reader = BackwardsReader::new(10, &mut writer);
26/// reader.read_all(&mut writer);
27/// writer.flush().unwrap();
28/// ```
29pub struct BackwardsReader<'a> {
30    pieces: VecDeque<VecDeque<Vec<u8>>>,
31    num_of_lines: usize,
32    fd: &'a mut BufReader<File>,
33    total_newlines: usize,
34    first_read: bool,
35    last_offset: u64
36}
37
38impl<'a> BackwardsReader<'a> {
39    pub fn new(num_of_lines: usize, fd: &'a mut BufReader<File>) -> Self {
40        let last_offset = fd.seek(SeekFrom::End(0))
41                                .unwrap_or_else(|_| { panic!("Failed to seek to end of file") });
42        BackwardsReader {
43            pieces: VecDeque::with_capacity(num_of_lines),
44            num_of_lines: num_of_lines,
45            fd: fd,
46            total_newlines: 0,
47            first_read: true,
48            last_offset: last_offset
49        }
50    }
51
52    fn handle_partial_read(&mut self) {
53        if self.last_offset > 0 {
54            self.fd.seek(SeekFrom::Start(0)).unwrap();
55            let mut buff = vec![0; (self.last_offset) as usize];
56            self.fd.read_exact(buff.as_mut_slice())
57                .unwrap_or_else(|_| { panic!("Incorrectly handled unexpected EOF. Probably an off by one error") });
58            if self.first_read && buff[buff.len() - 1] != b'\n' {
59                self.total_newlines += 1;
60                self.first_read = false;
61                buff.push(b'\n');
62            }
63            let buff: VecDeque<Vec<u8>> = buff.split(|elm: &u8| {*elm == b'\n'}).map(|elm: &[u8]| elm.to_vec()).collect();
64            self.total_newlines += buff.len() - 1;
65            self.pieces.push_front(buff);
66        }
67    }
68
69    fn read(&mut self) -> bool {
70        let seek_offset = if (self.last_offset as i64) - (BUFFER_SIZE as i64) >= 0 {
71            self.last_offset - BUFFER_SIZE
72        } else {
73            self.handle_partial_read();
74            return false;
75        };
76        match self.fd.seek(SeekFrom::Start(seek_offset)) {
77            Ok(new_offset) => {
78                self.last_offset = new_offset;
79            },
80            Err(_) => {
81                self.handle_partial_read();
82                return false;
83            }
84        }
85
86        let mut buff = vec![0; BUFFER_SIZE as usize];
87        self.fd.read_exact(buff.as_mut_slice())
88            .unwrap_or_else(|_| { panic!("Failed to read from end of file in BackwardsReader") });
89        if self.first_read && buff[buff.len() - 1] != b'\n' {
90            self.total_newlines += 1;
91            self.first_read = false;
92            buff.push(b'\n');
93        }
94        let buff: VecDeque<Vec<u8>> = buff.split(|elm: &u8| {*elm == b'\n'}).map(|elm: &[u8]| elm.to_vec()).collect();
95        self.total_newlines += buff.len() - 1;
96        self.pieces.push_front(buff);
97        
98        if self.first_read {
99            self.first_read = false;
100        }
101        self.total_newlines < self.num_of_lines
102    }
103
104    pub fn read_all<T: Write>(&mut self, writer: &mut BufWriter<T>) {
105        while self.read() {}
106
107        // If we hit the top of the file early, there's no guarantee
108        // that total_newlines will be greater than num_of_lines due
109        // to the way failed backward seeks are handled in read()
110        if self.total_newlines > self.num_of_lines {
111            let mut first_chunk = self.pieces.pop_front().unwrap();
112            let pieces_to_discard = self.total_newlines - self.num_of_lines as usize;
113            if pieces_to_discard > 0 {
114                for _ in 0..pieces_to_discard {
115                    first_chunk.pop_front().unwrap();
116                }
117                self.total_newlines -= pieces_to_discard;
118            }
119            self.pieces.push_front(first_chunk);
120        }
121
122        if self.pieces.is_empty() { return; }
123
124        let mut line: Vec<u8> = Vec::new();
125        while let Some(mut piece) = self.pieces.pop_front() {
126            if piece.len() == 1 {
127                line.append(piece.pop_front().unwrap().as_mut());
128            } else if piece.len() > 1 {
129                let mut last_chunk = piece.pop_back().unwrap();
130                for mut chunk in piece {
131                    line.append(&mut chunk);
132                    line.push(b'\n');
133                    writer.write(&line).unwrap();
134                    line.clear();
135                }
136                line.append(&mut last_chunk);
137            }
138        }
139        if !line.is_empty() {
140            writer.write(&line).unwrap();
141        }
142    }
143}
144
145#[derive(Debug)]
146pub struct StatefulFile {
147    pub fd: BufReader<File>,
148    pub old_metadata: Metadata,
149    file_name: String,
150    cursor: SeekFrom,
151}
152
153impl StatefulFile {
154    pub fn new(fd: File, file_name: String) -> Self {
155        StatefulFile {
156            old_metadata: fd.metadata()
157                .unwrap_or_else(|_| { panic!("Could not retrieve metadata for file: {}", &file_name) }),
158            fd: BufReader::new(fd),
159            file_name: file_name,
160            cursor: SeekFrom::Start(0),
161        }
162    }
163
164    pub fn update_metadata(&mut self) {
165        self.old_metadata = self.fd.get_ref().metadata()
166            .unwrap_or_else(|_| { panic!("Could not retrieve metadata for file: {}", self.file_name) });
167    }
168
169    pub fn modification_type(&self) -> ModificationType {
170        let new_metadata = self.fd.get_ref().metadata()
171            .unwrap_or_else(|_| { panic!("Could not retrieve metadata for file: {}", self.file_name) });
172        if new_metadata.len() > self.old_metadata.len() {
173            ModificationType::Added
174        } else if new_metadata.len() < self.old_metadata.len() {
175            ModificationType::Removed
176        } else {
177            ModificationType::NoChange
178        }
179    }
180
181    pub fn seek_to_cursor(&mut self) {
182        self.fd.seek(self.cursor).unwrap();
183    }
184
185    pub fn update_cursor(&mut self) {
186        self.cursor = SeekFrom::Start(self.fd.seek(SeekFrom::Current(0)).unwrap());
187    }
188
189    pub fn reset_cursor(&mut self) {
190        self.cursor = SeekFrom::Start(0);
191    }
192}