diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index 4a6d4961..b7891343 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -137,6 +137,12 @@ impl App { } }); + if self.matches.value_of("file-name").is_some() + && self.matches.values_of("file-name").unwrap().len() != files.len() + { + return Err("When using --file-name, each input file must have a corresponding --file-name specified.".into()); + } + Ok(Config { true_color: is_truecolor_terminal(), language: self.matches.value_of("language").or_else(|| { @@ -222,7 +228,10 @@ impl App { .transpose()? .unwrap_or_else(|| vec![LineRange { lower: 0, upper: 0 }]), ), - filename: self.matches.value_of("file-name").or_else(|| None), + filenames: self + .matches + .values_of("file-name") + .map(|values| values.collect()), }) } diff --git a/src/controller.rs b/src/controller.rs index ca2b9cfb..96633133 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -41,7 +41,20 @@ impl<'b> Controller<'b> { let stdin = io::stdin(); - for input_file in &self.config.files { + let filenames = if self.config.filenames.is_none() { + vec![None; self.config.files.len()] + } else { + self.config + .filenames + .as_ref() + .unwrap() + .into_iter() + .map(|name| Some(*name)) + .collect() + }; + + for it in self.config.files.iter().zip(filenames) { + let (input_file, file_name) = it; match input_file.get_reader(&stdin) { Err(error) => { handle_error(&error); @@ -50,7 +63,7 @@ impl<'b> Controller<'b> { Ok(mut reader) => { let result = if self.config.loop_through { let mut printer = SimplePrinter::new(); - self.print_file(reader, &mut printer, writer, *input_file) + self.print_file(reader, &mut printer, writer, *input_file, file_name) } else { let mut printer = InteractivePrinter::new( &self.config, @@ -58,7 +71,7 @@ impl<'b> Controller<'b> { *input_file, &mut reader, ); - self.print_file(reader, &mut printer, writer, *input_file) + self.print_file(reader, &mut printer, writer, *input_file, file_name) }; if let Err(error) = result { @@ -78,9 +91,10 @@ impl<'b> Controller<'b> { printer: &mut P, writer: &mut dyn Write, input_file: InputFile<'a>, + file_name: Option<&str>, ) -> Result<()> { if !reader.first_line.is_empty() || self.config.output_components.header() { - printer.print_header(writer, input_file)?; + printer.print_header(writer, input_file, file_name)?; } if !reader.first_line.is_empty() { diff --git a/src/lib.rs b/src/lib.rs index 437288b4..5723145d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,5 +131,5 @@ pub struct Config<'a> { pub highlight_lines: LineRanges, /// Name of file to display when printing - pub filename: Option<&'a str>, + pub filenames: Option>, } diff --git a/src/printer.rs b/src/printer.rs index 0c5f7fdc..64d42d1d 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -34,7 +34,12 @@ use crate::terminal::{as_terminal_escaped, to_ansi_color}; use crate::Config; pub trait Printer { - fn print_header(&mut self, handle: &mut dyn Write, file: InputFile) -> Result<()>; + fn print_header( + &mut self, + handle: &mut dyn Write, + file: InputFile, + file_name: Option<&str>, + ) -> Result<()>; fn print_footer(&mut self, handle: &mut dyn Write) -> Result<()>; fn print_snip(&mut self, handle: &mut dyn Write) -> Result<()>; @@ -57,7 +62,12 @@ impl SimplePrinter { } impl Printer for SimplePrinter { - fn print_header(&mut self, _handle: &mut dyn Write, _file: InputFile) -> Result<()> { + fn print_header( + &mut self, + _handle: &mut dyn Write, + _file: InputFile, + _file_name: Option<&str>, + ) -> Result<()> { Ok(()) } @@ -224,15 +234,20 @@ impl<'a> InteractivePrinter<'a> { } impl<'a> Printer for InteractivePrinter<'a> { - fn print_header(&mut self, handle: &mut dyn Write, file: InputFile) -> Result<()> { + fn print_header( + &mut self, + handle: &mut dyn Write, + file: InputFile, + file_name: Option<&str>, + ) -> Result<()> { if !self.config.output_components.header() { if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable { let input = match file { InputFile::Ordinary(filename) => format!( "file '{}'", - self.config.filename.unwrap_or(&filename.to_string_lossy()) + file_name.unwrap_or(&filename.to_string_lossy()) ), - _ => self.config.filename.unwrap_or("STDIN").to_owned(), + _ => file_name.unwrap_or("STDIN").to_owned(), }; writeln!( @@ -269,17 +284,9 @@ impl<'a> Printer for InteractivePrinter<'a> { let (prefix, name) = match file { InputFile::Ordinary(filename) => ( "File: ", - Cow::from( - self.config - .filename - .unwrap_or(&filename.to_string_lossy()) - .to_owned(), - ), - ), - _ => ( - "File: ", - Cow::from(self.config.filename.unwrap_or("STDIN").to_owned()), + Cow::from(file_name.unwrap_or(&filename.to_string_lossy()).to_owned()), ), + _ => ("File: ", Cow::from(file_name.unwrap_or("STDIN").to_owned())), }; let mode = match self.content_type { diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index f2722339..7750f6cd 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -598,3 +598,32 @@ fn filename_stdin_binary() { .stdout("File: foo \n") .stderr(""); } + +#[test] +fn filename_multiple_ok() { + bat() + .arg("--decorations=always") + .arg("--style=header") + .arg("-r=0:0") + .arg("test.txt") + .arg("--file-name=foo") + .arg("single-line.txt") + .arg("--file-name=bar") + .assert() + .success() + .stdout("File: foo\nFile: bar\n") + .stderr(""); +} + +#[test] +fn filename_multiple_err() { + bat() + .arg("--decorations=always") + .arg("--style=header") + .arg("-r=0:0") + .arg("test.txt") + .arg("--file-name=foo") + .arg("single-line.txt") + .assert() + .failure(); +}