@@ -8,13 +8,14 @@ use std::collections::VecDeque;
88use std:: error:: Error ;
99use std:: fmt:: Display ;
1010use std:: fs:: File ;
11- use std:: io:: { self , BufRead , BufReader , ErrorKind , Read , Seek , SeekFrom , StdoutLock , Write } ;
11+ use std:: io:: {
12+ self , BufRead , BufReader , ErrorKind , Read , Seek , SeekFrom , StdinLock , StdoutLock , Write ,
13+ } ;
1214use std:: path:: PathBuf ;
1315use std:: str:: FromStr ;
1416use std:: sync:: mpsc;
1517use std:: time:: Duration ;
1618
17- #[ derive( Clone ) ]
1819enum RelativeFrom {
1920 StartOfFile ( usize ) ,
2021 EndOfFile ( usize ) ,
@@ -75,6 +76,7 @@ impl FromStr for RelativeFrom {
7576}
7677
7778/// tail - copy the last part of a file
79+ /// If neither -n nor -c are specified, copies the last 10 lines (-n 10).
7880#[ derive( Parser ) ]
7981#[ command( version, about) ]
8082struct Args {
@@ -120,6 +122,24 @@ impl Args {
120122 }
121123}
122124
125+ enum FileOrStdin {
126+ File ( PathBuf , BufReader < File > ) ,
127+ Stdin ( StdinLock < ' static > ) ,
128+ }
129+
130+ impl FileOrStdin {
131+ fn get_buf_read ( & mut self ) -> & mut dyn BufRead {
132+ match self {
133+ Self :: File ( _, ref mut bu) => bu,
134+ Self :: Stdin ( ref mut st) => st,
135+ }
136+ }
137+ }
138+
139+ fn print_bytes ( stdout_lock : & mut StdoutLock , bytes : & [ u8 ] ) -> io:: Result < ( ) > {
140+ stdout_lock. write_all ( bytes)
141+ }
142+
123143fn print_n_lines < R : Read + BufRead > (
124144 stdout_lock : & mut StdoutLock ,
125145 read : & mut R ,
@@ -263,7 +283,7 @@ fn print_n_bytes<R: Read>(
263283 let mut buffer_1 = vec ! [ 0_u8 ; n] ;
264284
265285 // Buffer to store the last `n` bytes read.
266- let mut buffer_2 = vec ! [ ] ;
286+ let mut buffer_2 = Vec :: < u8 > :: new ( ) ;
267287
268288 // Continuously read bytes into buffer_1.
269289 loop {
@@ -334,19 +354,12 @@ fn print_n_bytes<R: Read>(
334354 Ok ( ( ) )
335355}
336356
337- fn print_bytes ( stdout_lock : & mut StdoutLock , bytes : & [ u8 ] ) -> io:: Result < ( ) > {
338- stdout_lock. write_all ( bytes)
339- }
340-
341357/// The main logic for the `tail` command.
342358///
343359/// This function processes the command-line arguments to determine how many lines or bytes
344360/// to print from the end of a specified file or standard input. It supports an option to
345361/// follow the file, printing new data as it is appended to the file.
346362///
347- /// # Arguments
348- /// * `args` - The command-line arguments parsed into an `Args` struct.
349- ///
350363/// # Returns
351364/// * `Ok(())` - If the operation completes successfully.
352365/// * `Err(Box<dyn std::error::Error>)` - If an error occurs during the operation.
@@ -356,54 +369,54 @@ fn print_bytes(stdout_lock: &mut StdoutLock, bytes: &[u8]) -> io::Result<()> {
356369/// - The specified file cannot be opened.
357370/// - An error occurs while reading from the file or stdin.
358371/// - An error occurs while watching the file for changes.
359- ///
360372fn tail (
361373 file : Option < PathBuf > ,
362374 follow : bool ,
363375 bytes_or_lines : BytesOrLines ,
364376) -> Result < ( ) , Box < dyn Error > > {
365- fn get_stdin ( ) -> ( Box < dyn Read > , bool ) {
366- ( Box :: new ( io:: stdin ( ) . lock ( ) ) , false )
377+ fn get_stdin ( ) -> FileOrStdin {
378+ FileOrStdin :: Stdin ( io:: stdin ( ) . lock ( ) )
367379 }
368380
369- // open file, or stdin
370- let ( box_dyn_read, reading_from_file) : ( Box < dyn Read > , bool ) = match & file {
381+ let mut file_or_stdin = match file {
371382 Some ( pa) => {
372383 if pa. as_os_str ( ) == "-" {
373384 get_stdin ( )
374385 } else {
375- ( Box :: new ( File :: open ( pa) ?) , true )
386+ let fi = File :: open ( pa. as_path ( ) ) ?;
387+
388+ FileOrStdin :: File ( pa, BufReader :: new ( fi) )
376389 }
377390 }
378391 None => get_stdin ( ) ,
379392 } ;
380393
381- let mut buf_reader = io:: BufReader :: new ( box_dyn_read) ;
382-
383394 let mut stdout_lock = io:: stdout ( ) . lock ( ) ;
384395
385- match bytes_or_lines {
386- BytesOrLines :: Bytes ( re) => {
387- print_n_bytes ( & mut stdout_lock, & mut buf_reader, re) ?;
388- }
389- BytesOrLines :: Lines ( re) => {
390- print_n_lines ( & mut stdout_lock, & mut buf_reader, re) ?;
396+ {
397+ let mut buf_reader = file_or_stdin. get_buf_read ( ) ;
398+
399+ match bytes_or_lines {
400+ BytesOrLines :: Bytes ( re) => {
401+ print_n_bytes ( & mut stdout_lock, & mut buf_reader, re) ?;
402+ }
403+ BytesOrLines :: Lines ( re) => {
404+ print_n_lines ( & mut stdout_lock, & mut buf_reader, re) ?;
405+ }
391406 }
392407 }
393408
394- // If follow option is specified, continue monitoring the file
395- if let Some ( pa) = file {
396- if follow && reading_from_file {
397- // Opening a file and placing the cursor at the end of the file
398- let mut file = File :: open ( pa. as_path ( ) ) ?;
399- file. seek ( SeekFrom :: End ( 0 ) ) ?;
400-
401- let mut reader = BufReader :: new ( & file) ;
409+ if follow {
410+ // If follow option is specified, continue monitoring the file
411+ if let FileOrStdin :: File ( pa, mut bu) = file_or_stdin {
412+ // Move the the cursor to the end of the file
413+ // Is this still necessary now that the `BufReader` and underlying `File` are being reused?
414+ bu. seek ( SeekFrom :: End ( 0_i64 ) ) ?;
402415
403416 let ( tx, rx) = mpsc:: channel ( ) ;
404417
405418 // Automatically select the best implementation for your platform.
406- let mut debouncer = new_debouncer ( Duration :: from_millis ( 1 ) , None , tx) . unwrap ( ) ;
419+ let mut debouncer = new_debouncer ( Duration :: from_millis ( 1_u64 ) , None , tx) . unwrap ( ) ;
407420
408421 // Add a path to be watched.
409422 // below will be monitored for changes.
@@ -420,19 +433,19 @@ fn tail(
420433 | EventKind :: Modify ( ModifyKind :: Data ( _) )
421434 | EventKind :: Modify ( ModifyKind :: Other ) => {
422435 // If the file has been modified, check if the file was truncated
423- let metadata = file . metadata ( ) ?;
436+ let metadata = bu . get_mut ( ) . metadata ( ) ?;
424437 let current_size = metadata. len ( ) ;
425438
426- if current_size < reader . stream_position ( ) ? {
439+ if current_size < bu . stream_position ( ) ? {
427440 eprintln ! ( "\n tail: {}: file truncated" , pa. display( ) ) ;
428441
429- reader . seek ( SeekFrom :: Start ( 0_u64 ) ) ?;
442+ bu . seek ( SeekFrom :: Start ( 0_u64 ) ) ?;
430443 }
431444
432445 // Read the new lines and output them
433- let mut new_data = vec ! [ ] ;
446+ let mut new_data = Vec :: < u8 > :: new ( ) ;
434447
435- let bytes_read = reader . read_to_end ( & mut new_data) ?;
448+ let bytes_read = bu . read_to_end ( & mut new_data) ?;
436449
437450 if bytes_read > 0_usize {
438451 print_bytes ( & mut stdout_lock, & new_data) ?;
@@ -447,7 +460,7 @@ fn tail(
447460 }
448461 }
449462 Err ( ve) => {
450- eprintln ! ( "watch error: {ve:?}" ) ;
463+ eprintln ! ( "tail: watch error: {ve:?}" ) ;
451464 }
452465 }
453466 }
@@ -473,10 +486,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
473486 }
474487 } ;
475488
476- let mut exit_code = 0 ;
489+ let mut exit_code = 0_i32 ;
477490
478491 if let Err ( er) = tail ( args. file , args. follow , bytes_or_lines) {
479- exit_code = 1 ;
492+ exit_code = 1_i32 ;
480493
481494 eprintln ! ( "tail: {}" , er) ;
482495 }
0 commit comments