use std::{fs::File, io};
#[cfg(not(feature = "fast-lines"))]
use std::io::{BufReader, Read};
#[cfg(feature = "fast-lines")]
use {memmap2::Mmap, rayon::prelude::*};
pub trait FileExtension {
fn count_lines(&self) -> io::Result<u64>;
}
impl FileExtension for File {
#[cfg(feature = "fast-lines")]
fn count_lines(&self) -> io::Result<u64> {
let mmap = unsafe { Mmap::map(self)? };
let count = mmap.par_iter().filter(|&&byte| byte == b'\n').count();
Ok(count as u64)
}
#[cfg(not(feature = "fast-lines"))]
fn count_lines(&self) -> io::Result<u64> {
BufReader::new(self)
.bytes() .try_fold(0u64, |acc, byte_result| {
byte_result.map(|b| if b == b'\n' { acc + 1 } else { acc })
})
}
}
pub trait IteratorBack {
fn skip_last(self) -> Self;
fn skip_back(self, n: usize) -> Self;
}
impl<I> IteratorBack for I
where
I: DoubleEndedIterator,
{
fn skip_last(mut self) -> Self {
let _last = self.next_back();
self
}
fn skip_back(mut self, n: usize) -> Self {
if n > 0 {
let _last_n = self.nth_back(n - 1);
}
self
}
}
#[cfg(test)]
mod test_skip_last {
use super::*;
use std::iter;
#[test]
fn test_empty() {
let iter = iter::empty::<i32>();
let result: Vec<_> = iter.skip_last().collect();
assert_eq!(result, []);
}
#[test]
fn test_single_element() {
let data = [1];
let result: Vec<_> = data.into_iter().skip_last().collect();
assert_eq!(result, []);
}
#[test]
fn test_multiple_elements() {
let data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let result: Vec<_> = data
.into_iter()
.skip(1)
.skip_last()
.skip(1)
.skip_last()
.skip(1)
.collect();
assert_eq!(result, [4, 5, 6, 7]);
}
#[test]
fn test_split_and_skip_last_and_skip() {
let line = " | field_1| field_2 |field_3 | ";
let data: Vec<_> = line
.split('|')
.skip_last() .skip(1)
.map(|field| field.trim().to_string())
.collect();
assert_eq!(data, ["field_1", "field_2", "field_3"]);
}
}
#[cfg(test)]
mod test_skip_back {
use super::*;
use std::iter;
#[test]
fn test_empty() {
let iter = iter::empty::<i32>();
let result: Vec<_> = iter.skip_back(1).collect();
assert_eq!(result, []);
}
#[test]
fn test_single_element() {
let data = [1];
let result: Vec<_> = data.into_iter().skip_back(0).collect();
assert_eq!(result, [1]);
let data = [1];
let result: Vec<_> = data.into_iter().skip_back(1).collect();
assert_eq!(result, []);
}
#[test]
fn test_multiple_elements() {
let data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let result: Vec<_> = data
.into_iter()
.skip(1)
.skip_back(1)
.skip(1)
.skip_back(2)
.skip(1)
.collect();
assert_eq!(result, [4, 5, 6]);
}
#[test]
fn test_split_and_skip_last_and_skip() {
let line = " | field_1| field_2 |field_3 | ";
let data: Vec<_> = line
.split('|')
.skip_back(1) .skip(1)
.map(|field| field.trim().to_string())
.collect();
assert_eq!(data, ["field_1", "field_2", "field_3"]);
}
}
#[cfg(test)]
mod count_lines_tests {
use super::*;
use std::io::{Seek, Write};
#[test]
fn test_count_lines() -> io::Result<()> {
let path = "/tmp/sample_lines.txt";
{
let mut file = File::create(path)?;
let lines = "Line 1\nLine 2\nLine 3\nLine 4\n";
println!("lines:\n{}", lines);
file.write_all(lines.as_bytes())?;
}
let mut file = File::open(path)?;
let number_of_lines: u64 = file.count_lines()?;
file.rewind()?;
println!("Calculated lines: {}", number_of_lines);
assert_eq!(number_of_lines, 4);
std::fs::remove_file(path)?;
Ok(())
}
}