1use std::fs::File;
6use std::io::{BufRead, BufReader, Read};
7use std::path::Path;
8
9use crate::error::Error;
10
11pub const DEFAULT_LINES: usize = 10;
12
13#[derive(Debug, Default, Clone, Copy, PartialEq)]
14pub struct Options {
15 pub bytes: Option<usize>,
17
18 pub lines: Option<usize>,
21}
22
23impl Options {
24 pub fn with_bytes(bytes: usize) -> Self {
25 Self {
26 bytes: Some(bytes),
27 lines: None,
28 }
29 }
30
31 pub fn with_lines(lines: usize) -> Self {
32 Self {
33 lines: Some(lines),
34 bytes: None,
35 }
36 }
37}
38
39pub fn head<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
41 let mut fd = File::open(file)?;
42
43 if options.lines.is_none() && options.bytes.is_some() {
44 let mut bytes = options.bytes.unwrap();
45 const BUF_SIZE: usize = 4 * 1024;
46 let mut buf = [0; BUF_SIZE];
47 let mut n_read = 1;
48 let mut result = Vec::new();
49 while bytes > 0 && n_read > 0 {
50 let chunk_size = usize::min(BUF_SIZE, bytes);
51 n_read = fd.read(&mut buf[0..chunk_size])?;
52 bytes -= n_read;
53 result.extend_from_slice(&buf[0..n_read]);
54 }
55 let result = String::from_utf8(result)?;
56 return Ok(result);
57 }
58
59 let mut buf = BufReader::new(fd);
60 let mut result = String::new();
61 let mut lines = options.lines.unwrap_or(DEFAULT_LINES);
62 let mut n_read = 1;
63 let mut line = String::new();
64 while lines > 0 && n_read > 0 {
65 n_read = buf.read_line(&mut line)?;
66 result += &line;
67 lines -= 1;
68 }
69
70 Ok(result)
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn test_head() {
79 let input_file = "tests/Rust_Wikipedia.html";
80
81 assert!(head(input_file, &Options::default()).is_ok());
82 assert!(head(input_file, &Options::with_lines(20)).is_ok());
83 assert!(head(input_file, &Options::with_bytes(1024)).is_ok());
84 }
85}